-
Notifications
You must be signed in to change notification settings - Fork 29
/
comprehension.scala
153 lines (139 loc) · 5.8 KB
/
comprehension.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package com.thoughtworks.dsl
import scala.language.higherKinds
import scala.language.implicitConversions
import com.thoughtworks.dsl.keywords._
private[dsl] sealed trait LowPriorityComprehension0 {
import com.thoughtworks.dsl.comprehension._
implicit def comprehensionOps[From, Keyword, Value](from: From)(
implicit asKeyword: From => Dsl.Keyword[Keyword, Value] with Keyword): ComprehensionOps[Keyword, Value] =
new ComprehensionOps[Keyword, Value](from)
}
/** Provides utilities to perform `for`-comprehension on [[Dsl.Keyword]].
*
*
* Add the following import statement to enable `for`-comprehension on keywords:
*
* {{{
* import com.thoughtworks.dsl.comprehension._
* }}}
*
* @example `for` / `yield` expressions can be used on keywords.
*
* {{{
* import com.thoughtworks.dsl.keywords._
*
* def cartesianProduct = for {
* i <- Each(Array(1, 2, 3))
* j <- Each(Vector(1, 10, 100, 1000))
* } yield i * j
* }}}
*
* The results of `for` / `yield` expressions are also keywords.
*
* {{{
* cartesianProduct should be(a[Dsl.Keyword[_, _]])
* }}}
*
* You can use !-notation extract the value from the produced keyword.
*
* {{{
* def resultAsList = List(!cartesianProduct)
* resultAsList should be(List(1, 10, 100, 1000, 2, 20, 200, 2000, 3, 30, 300, 3000))
*
* def resultAsSet: Set[Int] = !Return(!cartesianProduct)
* resultAsSet should be(Set(1, 10, 100, 1000, 2, 20, 200, 2000, 3, 30, 300, 3000))
* }}}
*
* Alternatively, [[comprehension.ComprehensionOps.to]] can be used to convert the result of a keyword to other types of values as well.
*
* {{{
* cartesianProduct.to[List] should be(List(1, 10, 100, 1000, 2, 20, 200, 2000, 3, 30, 300, 3000))
* }}}
*
* @example This example implements the same feature as the example on Scaladoc of [[keywords.Yield]],
* except this example use `for`-comprehension instead of !-notation.
*
* {{{
* import com.thoughtworks.dsl.Dsl
* import com.thoughtworks.dsl.keywords._
* import com.thoughtworks.dsl.comprehension._
*
* def gccFlagBuilder(sourceFile: String, includes: String*) = {
* for {
* _ <- Yield("gcc")
* _ <- Yield("-c")
* _ <- Yield(sourceFile)
* include <- Each(includes)
* _ <- Yield("-I")
* _ <- Yield(include)
* r <- Continue
* } yield r: String
* }
*
* gccFlagBuilder("main.c", "lib1/include", "lib2/include").to[Stream] should be(Stream("gcc", "-c", "main.c", "-I", "lib1/include", "-I", "lib2/include"))
* }}}
*
* Alternatively, you can use Scala native `yield` keyword to produce the last value.
*
* {{{
* def gccFlagBuilder2(sourceFile: String, includes: String*) = {
* for {
* _ <- Yield("gcc")
* _ <- Yield("-c")
* _ <- Yield(sourceFile)
* include <- Each(includes)
* _ <- Yield("-I")
* } yield include
* }
* gccFlagBuilder2("main.c", "lib1/include", "lib2/include").to[Stream] should be(Stream("gcc", "-c", "main.c", "-I", "lib1/include", "-I", "lib2/include"))
* }}}
*
* You can also omit the explicit constructor of [[keywords.Yield]] with the help of implicit conversion [[keywords.Yield.implicitYield]].
*
* {{{
* import com.thoughtworks.dsl.keywords.Yield.implicitYield
*
* def augmentString = ()
* def wrapString = ()
* }}}
*
* Note that [[scala.Predef.augmentString]] and [[scala.Predef.wrapString]] must be disabled in order to use `flatMap` for [[keywords.Yield]].
*
* {{{
* def gccFlagBuilder3(sourceFile: String, includes: String*) = {
* for {
* _ <- "gcc"
* _ <- "-c"
* _ <- sourceFile
* include <- Each(includes)
* _ <- "-I"
* } yield include
* }
* gccFlagBuilder3("main.c", "lib1/include", "lib2/include").to[Stream] should be(Stream("gcc", "-c", "main.c", "-I", "lib1/include", "-I", "lib2/include"))
* }}}
*
*/
object comprehension extends LowPriorityComprehension0 {
@inline
private def resetDomain[Keyword, Domain](keyword: Keyword)(implicit dsl: Dsl[Keyword, Domain, Domain]): Domain = {
dsl.cpsApply(keyword, implicitly)
}
final class ComprehensionOps[Keyword, Value] private[dsl] (private val keyword: Keyword) extends AnyVal {
import keywords._
def map[MapResult](mapper: Value => MapResult): Map[Keyword, Value, MapResult] = Map(keyword, mapper)
def flatMap[MapResult, NestedKeyword, NestedValue](mapper: Value => MapResult)(
implicit asKeywordMapper: (
Value => MapResult) => Value => Dsl.Keyword[NestedKeyword, NestedValue] with NestedKeyword)
: FlatMap[Keyword, Value, NestedKeyword, NestedValue] = FlatMap(keyword, mapper)
def withFilter(condition: Value => Boolean): WithFilter[Keyword, Value] = WithFilter(keyword, condition)
def to[Output[_]](implicit dsl: Dsl[Keyword, Output[Value], Value],
returnDsl: Dsl[Return[Value], Output[Value], Nothing]): Output[Value] = {
dsl.cpsApply(keyword, { value: Value =>
resetDomain(Return(value))
})
}
}
implicit def nothingComprehensionOps[From, Keyword](from: From)(
implicit asKeyword: From => Dsl.Keyword[Keyword, Nothing] with Keyword): ComprehensionOps[Keyword, Nothing] =
new ComprehensionOps[Keyword, Nothing](from)
}