Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Let ToIterable support for...do and remove legacy keywords for the similar purpose #533

Merged
merged 8 commits into from
Dec 15, 2021
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
46 changes: 24 additions & 22 deletions Dsl/src/main/scala/com/thoughtworks/dsl/Dsl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ private[dsl] trait LowPriorityDsl0 extends LowPriorityDsl1 { this: Dsl.type =>
}

extension [KeywordOrView, Element](keywordOrView: KeywordOrView)(using hasValueOrElement: Dsl.HasValueOrElement[KeywordOrView, Element])
def flatMap[Mapped <: Comprehension.Container[MappedElement], MappedElement](flatMapper: Element => Mapped) = Comprehension.Container.FlatMap(keywordOrView, flatMapper)
def map[Mapped](mapper: Element => Mapped) = Comprehension.Container.Map(keywordOrView, mapper)
def foreach[Nested <: Comprehension.DiscardValue](action: Element => Nested) = Comprehension.DiscardValue.FlatForeach(keywordOrView, action)
def foreach(action: Element => Unit) = Comprehension.DiscardValue.Foreach(keywordOrView, action)
def withFilter(filter: Element => Boolean) = Comprehension.Container.WithFilter(keywordOrView, filter)
def flatMap[Mapped <: For.Yield[MappedElement], MappedElement](flatMapper: Element => Mapped) = For.Yield.FlatMap(keywordOrView, flatMapper)
def map[Mapped](mapper: Element => Mapped) = For.Yield.Map(keywordOrView, mapper)
def foreach[Nested <: For.Do](action: Element => Nested) = For.Do.FlatForeach(keywordOrView, action)
def foreach(action: Element => Unit) = For.Do.Foreach(keywordOrView, action)
def withFilter(filter: Element => Boolean) = For.Yield.WithFilter(keywordOrView, filter)
// TODO: Implement `foreach` and `map` in macros to support !-notation in `do` block or `yield` block

// // FIXME: Shift
Expand Down Expand Up @@ -108,29 +108,31 @@ object Dsl extends LowPriorityDsl0 {

sealed trait HasValueOrElement[KeywordOrView, ValueOrElement]
object HasValueOrElement {
given [KeywordOrView <: Comprehension.Container[Element], Element]: Comprehension.Container[Element] with {}
given [KeywordOrView <: For.Yield[Element], Element]: For.Yield[Element] with {}
}

/** The reified for-comprehension expression.
/** The AST returned from a `for`...`yield` or a `for`...`do` expression.
*
* Note that a [[Comprehension]] does not directly support !-notation.
* Instead, [[keywords.As]] is used to convert a [[Comprehension]] to a
* Note that a [[For]] does not directly support !-notation.
* Instead, [[keywords.ToView]] is used to convert a [[For]] to a
* [[Keyword]] that supports !-notation.
*/
sealed trait Comprehension
object Comprehension {
sealed trait DiscardValue extends Comprehension
object DiscardValue {
final case class KeywordForeach[Upstream, UpstreamElement, UnitKeyword](upstream: Upstream, action: UpstreamElement => UnitKeyword) extends DiscardValue
final case class Foreach[Upstream, UpstreamElement](upstream: Upstream, action: UpstreamElement => Unit) extends DiscardValue
final case class FlatForeach[Upstream, UpstreamElement, Nested <: DiscardValue](upstream: Upstream, action: UpstreamElement => Nested) extends DiscardValue
sealed trait For
object For {
/** The AST returned from a `for`...`do` expression. */
sealed trait Do extends For
object Do {
final case class KeywordForeach[Upstream, UpstreamElement, UnitKeyword](upstream: Upstream, action: UpstreamElement => UnitKeyword) extends Do
final case class Foreach[Upstream, UpstreamElement](upstream: Upstream, action: UpstreamElement => Unit) extends Do
final case class FlatForeach[Upstream, UpstreamElement, Nested <: Do](upstream: Upstream, action: UpstreamElement => Nested) extends Do
}
sealed trait Container[Element] extends Comprehension
object Container {
final case class KeywordMap[Upstream, UpstreamElement, ElementKeyword, Element](upstream: Upstream, mapper: UpstreamElement => ElementKeyword) extends Container[Element]
final case class Map[Upstream, UpstreamElement, Element](upstream: Upstream, mapper: UpstreamElement => Element) extends Container[Element]
final case class FlatMap[Upstream, UpstreamElement, Mapped <: Container[Element], Element](upstream: Upstream, flatMapper: UpstreamElement => Mapped) extends Container[Element]
final case class WithFilter[Upstream, Element](upstream: Upstream, filter: Element => Boolean) extends Container[Element]
/** The AST returned from a `for`...`yield` expression. */
sealed trait Yield[Element] extends For
object Yield {
final case class KeywordMap[Upstream, UpstreamElement, ElementKeyword, Element](upstream: Upstream, mapper: UpstreamElement => ElementKeyword) extends Yield[Element]
final case class Map[Upstream, UpstreamElement, Element](upstream: Upstream, mapper: UpstreamElement => Element) extends Yield[Element]
final case class FlatMap[Upstream, UpstreamElement, Mapped <: Yield[Element], Element](upstream: Upstream, flatMapper: UpstreamElement => Mapped) extends Yield[Element]
final case class WithFilter[Upstream, Element](upstream: Upstream, filter: Element => Boolean) extends Yield[Element]
}
}

Expand Down
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,14 @@ We also provide some built-in keywords, including:
* The `Shift` keyword for creating asynchronous tasks as delimited continuations, similar to the `shift` operator in [Scala Continuations](https://github.com/scala/scala-continuations).
* The `AsynchronousIo.Connect`, `AsynchronousIo.Accept`, `AsynchronousIo.Read` and `AsynchronousIo.Write` keywords for performing I/O on an asynchronous channel.
* The `Yield` keyword for generating lazy streams, similar to `yield` in C#, Python and JavaScript.
* The `Each` keyword for iterating on a collection, similar to the list comprehension feature in Scala, Haskell, OCaml, Python and Lisp.
* The `Continue` keyword LDK for skipping an element in an `Each` collection comprehension, similar to the native `continue` keyword in C/C++ or the `mzero` in Haskell.
* The `Fork` keyword for duplicating current context, similar to the `fork` system call in POSIX.
* The `Return` keyword for early returning, similar to the native `return` keyword in Scala.
* The `Using` keyword to automatically close resources when exiting a scope, similar to the native `using` keyword in C#.
* The `Monadic` keyword for creating Scalaz or Cats monadic control flow, similar to the !-notation in Idris.
* 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 `Each`, `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 `ToView.FromIterable`, `Using` and `Shift` together.

## Getting Started

Expand Down
44 changes: 5 additions & 39 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ lazy val `domains-Task` =
`keywords-Shift`,
reset,
`domains-Continuation`,
`keywords-In` % Test,
`keywords-Each` % Test,
`keywords-ToView` % Test,
`keywords-Using` % Test,
`keywords-Yield` % Test,
`keywords-ToView` % Test
Expand All @@ -44,18 +43,7 @@ lazy val `domains-Task` =
lazy val `keywords-ToView` =
crossProject(JSPlatform, JVMPlatform)
.crossType(CrossType.Pure)
.dependsOn(Dsl, reset, `keywords-Each`)

lazy val `keywords-Fork` =
crossProject(JSPlatform, JVMPlatform)
.crossType(CrossType.Pure)
.dependsOn(
Dsl,
reset,
`keywords-Shift`,
`keywords-ForEach`,
`keywords-In`
)
.dependsOn(Dsl, reset, `keywords-Shift`)

lazy val `keywords-Pure` =
crossProject(JSPlatform, JVMPlatform)
Expand Down Expand Up @@ -128,8 +116,7 @@ lazy val `keywords-AsynchronousIo` =
.crossType(CrossType.Pure)
.dependsOn(
`keywords-Shift`,
`keywords-In` % Test,
`keywords-Each` % Test,
`keywords-ToView` % Test,
`keywords-Using` % Test,
`keywords-ToView` % Test,
`domains-Task` % Test
Expand All @@ -155,16 +142,6 @@ lazy val `keywords-NoneSafe` =
.crossType(CrossType.Pure)
.dependsOn(Dsl, reset % Test, `keywords-Return`)

lazy val `keywords-For` =
crossProject(JSPlatform, JVMPlatform)
.crossType(CrossType.Pure)
.dependsOn(Dsl, reset % Test, `keywords-In` % Test)

lazy val `keywords-In` =
crossProject(JSPlatform, JVMPlatform)
.crossType(CrossType.Pure)
.dependsOn(Dsl, reset % Test, `keywords-Shift`)

lazy val `keywords-Await` =
crossProject(JSPlatform, JVMPlatform)
.crossType(CrossType.Pure)
Expand All @@ -173,7 +150,7 @@ lazy val `keywords-Await` =
`domains-Continuation`,
reset % Test,
`domains-Task` % Test,
`keywords-In` % Test,
`keywords-ToView` % Test,
`keywords-Get` % Test,
`keywords-Return` % Test,
`keywords-Yield` % Test,
Expand All @@ -191,9 +168,8 @@ lazy val `keywords-Yield` =
.dependsOn(
Dsl,
reset % Test,
`keywords-Each` % Test,
`keywords-ToView` % Test,
`keywords-Shift` % Test,
`keywords-In` % Test,
)

lazy val `keywords-Monadic` =
Expand All @@ -205,16 +181,6 @@ organization in ThisBuild := "com.thoughtworks.dsl"

skip in publish := true

lazy val `keywords-ForEach` =
crossProject(JSPlatform, JVMPlatform)
.crossType(CrossType.Pure)
.dependsOn(Dsl, reset % Test, `keywords-Each` % Test)

lazy val `keywords-Each` =
crossProject(JSPlatform, JVMPlatform)
.crossType(CrossType.Pure)
.dependsOn(Dsl, reset, `keywords-Shift`)

lazy val `domains-scalaz` =
crossProject(JSPlatform, JVMPlatform)
.crossType(CrossType.Pure)
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, Each}
import com.thoughtworks.dsl.keywords.{Using, ToView}
import com.thoughtworks.dsl.domains._
import com.thoughtworks.dsl.keywords.Shift
import com.thoughtworks.dsl.keywords.Shift.given
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 {
!Each(0 until 10) + !Shift(task1)
!ToView.FromIterable(0 until 10) + !Shift(task1)
}

!Shift(ts) should be(1 until 11)
Expand Down Expand Up @@ -180,11 +180,11 @@ final class taskSpec extends AsyncFreeSpec with Matchers {
def composeTask(t0: Task[Seq[Task[Seq[Task[Seq[Task[Seq[Float]]]]]]]]): Task[Seq[Seq[Seq[Seq[Float]]]]] = {
// TODO: remove explicit type parameters when https://github.com/scala/bug/issues/11068 is fixed
*[Task]/*.join*/ apply Seq {
val t1 = !Each(!Shift(t0))
val t1 = !ToView.FromIterable(!Shift(t0))
!Shift(*[Task]/*.join*/ apply Seq {
val t2 = !Each(!Shift(t1))
val t2 = !ToView.FromIterable(!Shift(t1))
!Shift(*[Task]/*.join*/ apply Seq {
val t3 = !Each(!Shift(t2))
val t3 = !ToView.FromIterable(!Shift(t2))
!Shift(t3)
})
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import scala.util.control.TailCalls.TailRec
*
* @template
* @example A [[Task]] can be created from `for`-comprehension,
* where [[keywords.Each]] can be used together to asynchronously iterate collections.
* where [[keywords.ToView.FromIterable]] can be used together to asynchronously iterate collections.
*
* For example, the above `concatenateRemoteData` downloads and concatenates data from multiple URLs.
*
Expand All @@ -31,9 +31,9 @@ import scala.util.control.TailCalls.TailRec
* import java.net.URL
* def concatenateRemoteData(urls: List[URL], downloader: URL => Task[Vector[Byte]]) = ToView {
* for {
* url <- Each(urls)
* url <- ToView.FromIterable(urls)
* data <- Shift(downloader(url))
* byte <- Each(data)
* byte <- ToView.FromIterable(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 Each]].
* [[https://github.com/ThoughtWorksInc/each ThoughtWorks ToView.FromIterable]].
*
* {{{
* 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.Each]],
* It asynchronously iterates elements `Seq`, `ArrayBuffer` and `String` with the help of [[keywords.ToView.FromIterable]],
* 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 @@ -53,11 +53,11 @@ import scala.util.control.NonFatal
* import java.net.URL
* def cat(paths: Path*) = ToView {
* for {
* path <- Each(paths)
* path <- ToView.FromIterable(paths)
* channel <- Using(AsynchronousFileChannel.open(path))
* charBuffers <- Shift(readAll(channel))
* charBuffer <- Each(charBuffers)
* char <- Each(charBuffer.toString)
* charBuffer <- ToView.FromIterable(charBuffers)
* char <- ToView.FromIterable(charBuffer.toString)
* } yield char
* }.to[Task]
* }}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,63 +31,27 @@ class AwaitTest extends AsyncFreeSpec with Matchers with Inside {
type Id[A] = A
"testComprehension1" in {
def inner1 = for {
j <- Each(0 until 3)
j <- ToView.FromIterable(0 until 3)
} yield 100 + j
summon[Run[
FlatMap[
In[Int],
Int,
Pure[Int]
],
Vector[Int],
Int
]]

summon[
AsKeyword.IsKeyword[
FlatMap[
Future[Int],
Int,
Pure[Int]
],
Int
]
]

val ast1 = Await(Future(1)).flatMap { i =>
inner1
}
summon[
ast1.type
<:<
Dsl.Comprehension.Container.FlatMap[
Dsl.For.Yield.FlatMap[
Await[Int],
Int,
Dsl.Comprehension.Container.Map[
Each[Int],
Dsl.For.Yield.Map[
ToView.FromIterable[Int],
Int,
Int
],
Int
]
]

def forall[A] = {
summon[Run[
FlatMap[
Await[Int],
Int,
FlatMap[
In[Int],
Int,
Pure[Int]
]
],
Future[A] !! Vector[Int],
Int
]]
}

*[Future] {
(!Await(
ToView(ast1).to[Future]
Expand All @@ -98,24 +62,24 @@ class AwaitTest extends AsyncFreeSpec with Matchers with Inside {
"testComprehension2" in {
import Dsl._
val inner2 = for {
j <- In(0 until 10)
j <- ToView.FromIterable(0 until 10)
} yield 111
summon[
inner2.type
<:<
Dsl.Comprehension.Container.Map[In[Int], Int, Int]
Dsl.For.Yield.Map[ToView.FromIterable[Int], Int, Int]
]
val ast2 = Await(Future(1)).flatMap { i =>
inner2
}
summon[
ast2.type
<:<
Dsl.Comprehension.Container.FlatMap[
Dsl.For.Yield.FlatMap[
Await[Int],
Int,
Dsl.Comprehension.Container.Map[
In[Int],
Dsl.For.Yield.Map[
ToView.FromIterable[Int],
Int,
Int
],
Expand All @@ -127,22 +91,21 @@ class AwaitTest extends AsyncFreeSpec with Matchers with Inside {

"testComprehension3" in {
import Dsl._
val ast3 = for {
val ast3 = ToView.toKeyword(for {
i <- Await(Future(1))
j <- In(0 until 10)
} yield 111
j <- ToView.FromIterable(0 until 10)
} yield 111)
summon[
ast3.type
<:<
Dsl.Comprehension.Container.FlatMap[
FlatMap[
Await[Int],
Int,
Dsl.Comprehension.Container.Map[
In[Int],
FlatMap[
ToView.FromIterable[Int],
Int,
Int
],
Int
Pure[collection.View[Int]]
]
]
]
succeed
Expand Down
Loading