Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dsl/src/main/scala/com/thoughtworks/dsl/Dsl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ object Dsl extends LowPriorityDsl0 {
/** The AST returned from a `for`...`yield` or a `for`...`do` expression.
*
* Note that a [[For]] does not directly support !-notation.
* Instead, [[keywords.FromIterable.ToView]] is used to convert a [[For]] to a
* Instead, [[keywords.Each.ToView]] is used to convert a [[For]] to a
* [[Keyword]] that supports !-notation.
*/
sealed trait For
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ We also provide some built-in keywords, including:
* The `NullSafe` keyword for the null safe operator, similar to the `?` operator in Kotlin and Groovy.
* The `NoneSafe` keyword for the `None` safe operator, similar to the `Maybe` monad in Haskell.

All the above keywords can be used together with each others. For example you can perform list comprehension to manipulate native resources in an asynchronous task by using `FromIterable`, `Using` and `Shift` together.
All the above keywords can be used together with each others. For example you can perform list comprehension to manipulate native resources in an asynchronous task by using `Each`, `Using` and `Shift` together.

## Getting Started

Expand Down
12 changes: 5 additions & 7 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,12 @@ lazy val `domains-Task` =
`keywords-Shift`,
reset,
`domains-Continuation`,
`keywords-FromIterable` % Test,
`keywords-Using` % Test,
`keywords-Yield` % Test,
`keywords-FromIterable` % Test
`keywords-Each` % Test
)

lazy val `keywords-FromIterable` =
lazy val `keywords-Each` =
crossProject(JSPlatform, JVMPlatform)
.crossType(CrossType.Pure)
.dependsOn(Dsl, reset % Test, `keywords-Pure`, `keywords-FlatMap`)
Expand Down Expand Up @@ -116,9 +115,8 @@ lazy val `keywords-AsynchronousIo` =
.crossType(CrossType.Pure)
.dependsOn(
`keywords-Shift`,
`keywords-FromIterable` % Test,
`keywords-Each` % Test,
`keywords-Using` % Test,
`keywords-FromIterable` % Test,
`domains-Task` % Test
)

Expand Down Expand Up @@ -153,7 +151,7 @@ lazy val `keywords-Await` =
`keywords-Get` % Test,
`keywords-Return` % Test,
`keywords-Yield` % Test,
`keywords-FromIterable` % Test
`keywords-Each` % Test
)

lazy val `scala-async` =
Expand All @@ -167,7 +165,7 @@ lazy val `keywords-Yield` =
.dependsOn(
Dsl,
reset % Test,
`keywords-FromIterable` % Test,
`keywords-Each` % Test,
`keywords-Shift` % Test,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import com.thoughtworks.dsl.Dsl.{!!}
import org.scalatest.Assertion
import scala.language.implicitConversions

import com.thoughtworks.dsl.keywords.{Using, FromIterable}
import com.thoughtworks.dsl.keywords.{Using, Each}
import com.thoughtworks.dsl.domains._
import com.thoughtworks.dsl.keywords.Shift

Expand Down Expand Up @@ -43,7 +43,7 @@ final class taskSpec extends AsyncFreeSpec with Matchers {
val task1: Task[Int] = Task.now(1)

val ts = *[Task]/* .join */ apply Seq {
!FromIterable(0 until 10) + !Shift(task1)
!Each(0 until 10) + !Shift(task1)
}

!Shift(ts) should be(1 until 11)
Expand Down Expand Up @@ -80,6 +80,19 @@ final class taskSpec extends AsyncFreeSpec with Matchers {
!Shift(task2) should be("try: my exception")
})

"try with Each" in {
val listTask = Task {
val x =
try {
List(10 * !Each(List(1, 2)))
} finally {}
x
}
Task.toFuture(listTask).map {
_ should be(List(10, 20))
}
}

"empty try" in {
val logs = ArrayBuffer.empty[String]

Expand Down Expand Up @@ -183,11 +196,11 @@ final class taskSpec extends AsyncFreeSpec with Matchers {
t0: Task[Seq[Task[Seq[Task[Seq[Task[Seq[Float]]]]]]]]
): Task[Seq[Seq[Seq[Seq[Float]]]]] = {
Task /*.join*/ apply Seq {
val t1 = !FromIterable(!Shift(t0))
val t1 = !Each(!Shift(t0))
!Shift(Task /*.join*/ apply Seq {
val t2 = !FromIterable(!Shift(t1))
val t2 = !Each(!Shift(t1))
!Shift(Task /*.join*/ apply Seq {
val t3 = !FromIterable(!Shift(t2))
val t3 = !Each(!Shift(t2))
!Shift(t3)
})
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import scala.util.control.TailCalls.TailRec
*
* @template
* @example A [[Task]] can be created from `for`-comprehension,
* where [[keywords.FromIterable]] can be used together to asynchronously iterate collections.
* where [[keywords.Each]] can be used together to asynchronously iterate collections.
*
* For example, the above `concatenateRemoteData` downloads and concatenates data from multiple URLs.
*
Expand All @@ -31,11 +31,11 @@ import scala.util.control.TailCalls.TailRec
* import com.thoughtworks.dsl.keywords._
* import com.thoughtworks.dsl.domains.Task
* import java.net.URL
* def concatenateRemoteData(urls: List[URL], downloader: URL => Task[Vector[Byte]]) = FromIterable.ToView {
* def concatenateRemoteData(urls: List[URL], downloader: URL => Task[Vector[Byte]]) = Each.ToView {
* for {
* url <- FromIterable(urls)
* url <- Each(urls)
* data <- Shift(downloader(url))
* byte <- FromIterable(data)
* byte <- Each(data)
* } yield byte
* }.to[Task]
* }}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import scala.util.control.NonFatal
* @example [[scalaz.Free.Trampoline]] is a monadic data type that performs tail call optimization.
* It can be built from a `@[[Dsl.reset reset]]` code block within some [[Dsl.Keyword#unary_$bang !-notation]],
* similar to the [[com.thoughtworks.each.Monadic.EachOps#each each]] method in
* [[https://github.com/ThoughtWorksInc/each ThoughtWorks FromIterable]].
* [[https://github.com/ThoughtWorksInc/each ThoughtWorks Each]].
*
* {{{
* import _root_.scalaz.Trampoline
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import scala.util.control.NonFatal
* and other keywords can be used together in the same `for` block.
*
* For example, the following `cat` function contains a single `for` block to concatenate file contents.
* It asynchronously iterates elements `Seq`, `ArrayBuffer` and `String` with the help of [[keywords.FromIterable]],
* It asynchronously iterates elements `Seq`, `ArrayBuffer` and `String` with the help of [[keywords.Each]],
* managed native resources with the help of [[keywords.Using]],
* performs previously created `readAll` task with the help of [[keywords.Shift]],
* and finally converts the return type [[comprehension.ComprehensionOps.as as]] a `Task[Vector[Char]]`.
Expand All @@ -51,13 +51,13 @@ import scala.util.control.NonFatal
* import com.thoughtworks.dsl.domains.Task
* import com.thoughtworks.dsl.Dsl.to
* import java.net.URL
* def cat(paths: Path*) = FromIterable.ToView {
* def cat(paths: Path*) = Each.ToView {
* for {
* path <- FromIterable(paths)
* path <- Each(paths)
* channel <- Using(AsynchronousFileChannel.open(path))
* charBuffers <- Shift(readAll(channel))
* charBuffer <- FromIterable(charBuffers)
* char <- FromIterable(charBuffer.toString)
* charBuffer <- Each(charBuffers)
* char <- Each(charBuffer.toString)
* } yield char
* }.to[Task]
* }}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class AwaitTest extends AsyncFreeSpec with Matchers with Inside {
type Id[A] = A
"testComprehension1" in {
def inner1 = for {
j <- FromIterable(0 until 3)
j <- Each(0 until 3)
} yield 100 + j

val ast1 = Await(Future(1)).flatMap { i =>
Expand All @@ -44,7 +44,7 @@ class AwaitTest extends AsyncFreeSpec with Matchers with Inside {
Await[Int],
Int,
Dsl.For.Yield.Map[
FromIterable[Int],
Each[Int],
Int,
Int
],
Expand All @@ -54,20 +54,20 @@ class AwaitTest extends AsyncFreeSpec with Matchers with Inside {

*[Future] {
(!Await(
FromIterable.ToView(ast1).to[Future]
Each.ToView(ast1).to[Future]
)).toVector should be(Vector(100, 101, 102))
}
}

"testComprehension2" in {
import Dsl._
val inner2 = for {
j <- FromIterable(0 until 10)
j <- Each(0 until 10)
} yield 111
summon[
inner2.type
<:<
Dsl.For.Yield.Map[FromIterable[Int], Int, Int]
Dsl.For.Yield.Map[Each[Int], Int, Int]
]
val ast2 = Await(Future(1)).flatMap { i =>
inner2
Expand All @@ -79,7 +79,7 @@ class AwaitTest extends AsyncFreeSpec with Matchers with Inside {
Await[Int],
Int,
Dsl.For.Yield.Map[
FromIterable[Int],
Each[Int],
Int,
Int
],
Expand All @@ -91,9 +91,9 @@ class AwaitTest extends AsyncFreeSpec with Matchers with Inside {

"testComprehension3" in {
import Dsl._
val ast3 = FromIterable.ToView.toKeyword(for {
val ast3 = Each.ToView.toKeyword(for {
i <- Await(Future(1))
j <- FromIterable(0 until 10)
j <- Each(0 until 10)
} yield 111)
summon[
ast3.type
Expand All @@ -102,7 +102,7 @@ class AwaitTest extends AsyncFreeSpec with Matchers with Inside {
Await[Int],
Int,
FlatMap[
FromIterable[Int],
Each[Int],
Int,
Pure[collection.View[Int]]
]
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,27 @@ import scala.collection.mutable.Builder
* 杨博 (Yang Bo)
*
* @example
* [[FromIterable]] keywords can be used to calculate cartesian product.
* [[Each]] keywords can be used to calculate cartesian product.
*
* {{{
* import com.thoughtworks.dsl.reset, reset._
* def cartesianProduct = reset (List(!FromIterable(Array(1, 2, 3)) * !FromIterable(Vector(1, 10, 100, 1000))))
* def cartesianProduct = reset (List(!Each(Array(1, 2, 3)) * !Each(Vector(1, 10, 100, 1000))))
* cartesianProduct should be(List(1, 10, 100, 1000, 2, 20, 200, 2000, 3, 30, 300, 3000))
* }}}
* @see
* [[Dsl.For]] if you want to use traditional `for` comprehension instead of
* !-notation.
*/
final case class FromIterable[Element](elements: Traversable[Element])
final case class Each[Element](elements: Traversable[Element])
extends Dsl.Keyword.Trait
object FromIterable {
object Each {
opaque type ToView[Comprehension] <: Dsl.Keyword.Opaque =
Dsl.Keyword.Opaque.Of[Comprehension]

object ToView {

def apply[Comprehension]
: Comprehension =:= FromIterable.ToView[Comprehension] =
: Comprehension =:= Each.ToView[Comprehension] =
Dsl.Keyword.Opaque.Of.apply

def toKeyword[ComprehensionOrKeyword, Keyword](
Expand Down Expand Up @@ -71,15 +71,15 @@ object FromIterable {
], FlatMap[
UpstreamKeyword,
collection.View[UpstreamElement],
FlatMap[FromIterable[
FlatMap[Each[
UpstreamElement
], UpstreamElement, NestedKeyword]
]] = { case Dsl.For.Do.FlatForeach(upstream, flatAction) =>
FlatMap(
upstreamToKeyword(upstream),
{ upstreamCollection =>
FlatMap(
FromIterable(upstreamCollection),
Each(upstreamCollection),
flatAction.andThen(mappedToKeyword)
)
}
Expand Down Expand Up @@ -107,15 +107,15 @@ object FromIterable {
], FlatMap[
UpstreamKeyword,
collection.View[UpstreamElement],
FlatMap[FromIterable[
FlatMap[Each[
UpstreamElement
], UpstreamElement, MappedKeyword]
]] = { case Dsl.For.Yield.FlatMap(upstream, flatMapper) =>
FlatMap(
upstreamToKeyword(upstream),
{ upstreamCollection =>
FlatMap(
FromIterable(upstreamCollection),
Each(upstreamCollection),
flatMapper.andThen(mappedToKeyword)
)
}
Expand Down Expand Up @@ -329,7 +329,7 @@ object FromIterable {
Keyword,
Value
]
): Dsl.AsKeyword.IsKeyword[FromIterable.ToView[Comprehension], Value]
): Dsl.AsKeyword.IsKeyword[Each.ToView[Comprehension], Value]
with {}

given [
Expand All @@ -349,13 +349,13 @@ object FromIterable {
Domain,
Value
]
): Dsl.PolyCont[FromIterable.ToView[Comprehension], Domain, Value] = {
): Dsl.PolyCont[Each.ToView[Comprehension], Domain, Value] = {
(as, handler) =>
polyCont.cpsApply(toKeyword(as), handler)
}
}

given [Element]: AsKeyword.IsKeyword[FromIterable[Element], Element] with {}
given [Element]: AsKeyword.IsKeyword[Each[Element], Element] with {}

extension [FA, A](inline fa: FA)(using
inline notKeyword: util.NotGiven[
Expand All @@ -364,7 +364,7 @@ object FromIterable {
inline asFA: FA <:< Traversable[A]
)
transparent inline def unary_! : A =
Dsl.shift(FromIterable(asFA(fa))): A
Dsl.shift(Each(asFA(fa))): A

private def toLinearSeq[Element](
i: IterableOnce[Element]
Expand Down Expand Up @@ -394,12 +394,12 @@ object FromIterable {
factory: Factory[MappedElement, MappedValue],
blockDsl: Dsl.PolyCont[MappedKeyword, Domain, MappedValue]
): Dsl.PolyCont[
FlatMap[FromIterable[Element], Element, MappedKeyword],
FlatMap[Each[Element], Element, MappedKeyword],
Domain,
MappedValue
] = {
case (
FlatMap(FromIterable(sourceCollection), flatMapper),
FlatMap(Each(sourceCollection), flatMapper),
handler
) =>
@inline def loop(
Expand Down Expand Up @@ -438,12 +438,12 @@ object FromIterable {
](using
blockDsl: Dsl.PolyCont[MappedKeyword, Domain, Unit]
): Dsl.PolyCont[
FlatMap[FromIterable[Element], Element, MappedKeyword],
FlatMap[Each[Element], Element, MappedKeyword],
Domain,
Unit
] = {
case (
FlatMap(FromIterable(sourceCollection), flatMapper),
FlatMap(Each(sourceCollection), flatMapper),
handler
) =>
@inline def loop(
Expand Down
Loading