package keywords
Contains built-in domain-specific Keywords and their corresponding interpreters.
- Source
- package.scala
- Alphabetic
- By Inheritance
- keywords
- AnyRef
- Any
- Hide All
- Show All
- Public
- All
Type Members
-
trait
AsynchronousIo[Value] extends Keyword[AsynchronousIo[Value], Value]
The base keyword to perform asynchronous IO in domains.task.Tasks.
The base keyword to perform asynchronous IO in domains.task.Tasks.
The following
readAll
is a Task to read file content with the help of AsynchronousIo.ReadFileimport java.nio._, file._, channels._ import com.thoughtworks.dsl.domains.task.Task import com.thoughtworks.dsl.keywords._ import com.thoughtworks.dsl.keywords.Shift._ import com.thoughtworks.dsl.keywords.AsynchronousIo.ReadFile import scala.collection.mutable.ArrayBuffer import scala.io.Codec def readAll(channel: AsynchronousFileChannel, temporaryBufferSize: Int = 4096): Task[ArrayBuffer[CharBuffer]] = Task { val charBuffers = ArrayBuffer.empty[CharBuffer] val decoder = Codec.UTF8.decoder val byteBuffer = ByteBuffer.allocate(temporaryBufferSize) var position: Long = 0L while (!ReadFile(channel, byteBuffer, position) != -1) { position += byteBuffer.position() byteBuffer.flip() charBuffers += decoder.decode(byteBuffer) byteBuffer.clear() } charBuffers }
Task
s created from !-notation can be used infor
-comprehension, and other keywords can be used together in the samefor
block. For example, the followingcat
function contains a singlefor
block to concatenate file contents. It asynchronously iterates elementsSeq
,ArrayBuffer
andString
with the help of keywords.Each, managed native resources with the help of keywords.Using, performs previously createdreadAll
task with the help of keywords.Shift, and finally converts the return type as aTask[Vector[Char]]
.import com.thoughtworks.dsl.comprehension._ import com.thoughtworks.dsl.keywords._ import com.thoughtworks.dsl.keywords.Shift._ import com.thoughtworks.dsl.domains.task.Task import java.net.URL def cat(paths: Path*) = { for { path <- Each(paths) channel <- Using(AsynchronousFileChannel.open(path)) charBuffers <- readAll(channel) charBuffer <- Each(charBuffers) char <- Each(charBuffer.toString) } yield char }.as[Task[Vector[Char]]]
Then the
cat
function is used to concatenate files from this project, as shown below:Task.toFuture(Task { (!cat(Paths.get(".sbtopts"), Paths.get(".scalafmt.conf"))).mkString should be( "-J-XX:MaxMetaspaceSize=512M\n-J-Xmx5G\n-J-Xss6M\nversion = \"1.5.1\"\nmaxColumn = 120" ) })
Example: -
final
case class
Await[Value](future: Future[Value]) extends AnyVal with Keyword[Await[Value], Value] with Product with Serializable
Await is a Keyword to extract value from a scala.concurrent.Future.
Await is a Keyword to extract value from a scala.concurrent.Future.
This keyword is available in functions whose return types are Future, domains.task.Task, or any exception aware continuations as
(_ !! Throwable !! _)
.Author:
杨博 (Yang Bo)
Given a Future:
import scala.concurrent.Future val myFuture40 = Future { 40 }
You can Await the Future in another Future
def myFuture42 = Future { !Await(myFuture40) + 2 }
A Future can be converted to a domains.task.Task with the help of Await.
import com.thoughtworks.dsl.domains.task.Task import com.thoughtworks.dsl.keywords.Await val myTask = Task { !Await(myFuture42) }
Then a domains.task.Task can be converted back to a scala.concurrent.Future via domains.task.Task.toFuture.
val myAssertionTask = Task { !Shift(myTask) should be(42) } Task.toFuture(myAssertionTask)
, !Await
can be used together withtry
/catch
/finally
.import scala.concurrent.Future val buffer = new StringBuffer def recoverFuture = Future { buffer.append("Oh") } def exceptionalFuture = Future[StringBuffer] { throw new IllegalStateException("No") } def myFuture = Future { try { !Await(exceptionalFuture) } catch { case e: IllegalStateException => !Await(recoverFuture) buffer.append(' ') buffer.append(e.getMessage) } finally { buffer.append("!") } } myFuture.map(_.toString should be("Oh No!"))
, Other keywords, including Return or Get, can be used together with Await
import scala.concurrent.Future import com.thoughtworks.dsl.keywords.{Get, Return} val buffer = new StringBuffer def recoverFuture = Future { buffer.append("Oh") } def exceptionalFuture = Future[StringBuffer] { throw new IllegalStateException("No") } def myFuture: Char => Future[StringBuffer] = !Return { try { !Await(exceptionalFuture) } catch { case e: IllegalStateException => !Await(recoverFuture) buffer.append(!Get[Char]) buffer.append(e.getMessage) } finally { buffer.append("!") } } myFuture(' ').map(_.toString should be("Oh No!"))
Examples: -
sealed
class
Continue extends Keyword[Continue, Nothing]
The base type of Continue keyword.
-
final
case class
Each[Element](elements: Traversable[Element]) extends Keyword[Each[Element], Element] with Product with Serializable
Iterates though each element in elements.
Iterates though each element in elements.
Author:
杨博 (Yang Bo)
Each keywords can be used to calculate cartesian product.
def cartesianProduct = 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 also
comprehension if you want to use traditional
for
comprehension instead of !-notation.
Example: - final case class FlatMap[UpstreamKeyword, UpstreamValue, NestedKeyword, NestedValue](upstream: UpstreamKeyword, flatMapper: (UpstreamValue) ⇒ NestedKeyword) extends Keyword[FlatMap[UpstreamKeyword, UpstreamValue, NestedKeyword, NestedValue], NestedValue] with Product with Serializable
-
final
case class
ForEach[Element](elements: Traversable[Element]) extends Keyword[ForEach[Element], Element] with Product with Serializable
Iterates though each element in elements.
- final case class Fork[Element](elements: Traversable[Element]) extends AnyVal with Keyword[Fork[Element], Element] with Product with Serializable
-
final
case class
Get[S]() extends Keyword[Get[S], S] with Product with Serializable
Author:
杨博 (Yang Bo)
- See also
- final case class Map[UpstreamKeyword, UpstreamValue, Value](upstream: UpstreamKeyword, mapper: (UpstreamValue) ⇒ Value) extends Keyword[Map[UpstreamKeyword, UpstreamValue, Value], Value] with Product with Serializable
-
final
case class
Monadic[F[_], A](fa: F[A]) extends Keyword[Monadic[F, A], A] with Product with Serializable
A keyword for extracting monadic value from the monadic expression fa.
A keyword for extracting monadic value from the monadic expression fa.
- To do
Monadic should be a scala.AnyVal after https://github.com/scala/bug/issues/10595 is resolved.
- See also
com.thoughtworks.dsl.domains.cats for using this Monadic keyword with cats.Monad.
com.thoughtworks.dsl.domains.scalaz for using this Monadic keyword with scalaz.Monad.
- final case class NoneSafe[A](option: Option[A]) extends AnyVal with Keyword[NoneSafe[A], A] with Product with Serializable
-
final
case class
NullSafe[A <: AnyRef](nullable: A @com.thoughtworks.dsl.Dsl.reset) extends AnyVal with Product with Serializable
NullSafe is a keyword to perform
null
check.NullSafe is a keyword to perform
null
check.Author:
杨博 (Yang Bo)
You can use ? annotation to represent a nullable value.
import com.thoughtworks.dsl.keywords.NullSafe._ case class Tree(left: Tree @ ? = null, right: Tree @ ? = null, value: String @ ? = null) val root: Tree @ ? = Tree( left = Tree( left = Tree(value = "left-left"), right = Tree(value = "left-right") ), right = Tree(value = "right") )
A normal
.
is not null safe, when selectingleft
,right
orvalue
on anull
value.a[NullPointerException] should be thrownBy { root.right.left.right.value }
The above code throws an exception because
root.right.left
isnull
. The exception can be avoided by using ? on a nullable value:root.?.right.?.left.?.right.?.value should be(null)
The entire expression will be
null
if one of ? is performed on anull
value.
The boundary of a null safe operator ? is the nearest enclosing expression whose type is annotated as@ ?
.("Hello " + ("world " + root.?.right.?.left.?.value)) should be("Hello world null") ("Hello " + (("world " + root.?.right.?.left.?.value.?): @ ?)) should be("Hello null") (("Hello " + ("world " + root.?.right.?.left.?.value.?)): @ ?) should be(null)
, The ? operator usually works with Java libraries that may produce
null
.import com.thoughtworks.dsl.keywords.NullSafe._ val myMap = new java.util.HashMap[String, String](); ((myMap.get("key1").? + myMap.get("key2").?): @ ?) should be(null)
- Note
The ? operator is only available on nullable values. A type is considered as nullable if it is a reference type, no matter it is annotated as
@ ?
or not.import com.thoughtworks.dsl.keywords.NullSafe._ val explicitNullable: String @ ? = null ((explicitNullable.? + " Doe") : @ ?) should be(null)
val implicitNullable: String = null ((implicitNullable.? + " Doe") : @ ?) should be(null)
A type is considered as not nullable if it is a value type.
val implicitNotNullable: Int = 0 "(implicitNotNullable.? + 42) : @ ?" shouldNot compile
Alternatively, a type can be considered as not nullable by explicitly converting it to NotNull.
val explicitNotNullable: NotNull[String] = NotNull("John") """(explicitNotNullable.? + " Doe") : @ ?""" shouldNot compile
- See also
NoneSafe for similar checks on scala.Options.
Examples: -
final
case class
Put[S](value: S) extends AnyVal with Keyword[Put[S], Unit] with Product with Serializable
Put is a Keyword to replace the value of the current context.
Put is a Keyword to replace the value of the current context.
Purely functional programming languages usually do not support native first-class mutable variables. In those languages, mutable states can be implemented in state monads.
Put and Get are the Dsl-based replacements of state monads.
We use unary function as the domain of mutable state. The parameter of the unary function can be read from the Get keyword, and changed by the Put keyword.
Author:
杨博 (Yang Bo)
The following example creates a function that accepts a string parameter and returns the upper-cased last character of the parameter.
def upperCasedLastCharacter: String => Char = { val initialValue = !Get[String]() !Put(initialValue.toUpperCase) val upperCased = !Get[String]() Function.const(upperCased.last) }
For example, given a string of
foo
, the upper-cased last character should beO
.// Output: O upperCasedLastCharacter("foo") should be('O')
, Put and Get support multiple states. The following code creates a formatter that Put parts of content into a
Vector[Any]
of string buffers.def formatter: Double => Int => Vector[Any] => String = { !Put(!Get[Vector[Any]] :+ "x=") !Put(!Get[Vector[Any]] :+ !Get[Double]) !Put(!Get[Vector[Any]] :+ ",y=") !Put(!Get[Vector[Any]] :+ !Get[Int]) !Return((!Get[Vector[Any]]).mkString) } formatter(0.5)(42)(Vector.empty) should be("x=0.5,y=42")
- See also
Examples: -
final
case class
Return[ReturnValue](returnValue: ReturnValue) extends AnyVal with Keyword[Return[ReturnValue], Nothing] with Product with Serializable
A Dsl.Keyword to early return a lifted value from the enclosing function.
A Dsl.Keyword to early return a lifted value from the enclosing function.
Author:
杨博 (Yang Bo)
Suppose you are generating a random integer less than 100, whose first digit and second digit is different. A solution is generating integers in an infinite loop, and Return from the loop when the generated integer conforms with requirements.
import scala.util.Random import scala.util.control.TailCalls import scala.util.control.TailCalls.TailRec def randomInt(): TailRec[Int] = { while (true) { val r = Random.nextInt(100) if (r % 10 != r / 10) { !Return(TailCalls.done(r)) } } throw new AssertionError("Unreachable code"); } val r = randomInt().result r should be < 100 r % 10 should not be r / 10
, Since this Return keyword can automatically lift the return type,
TailCalls.done
can be omitted.import scala.util.Random import scala.util.control.TailCalls import scala.util.control.TailCalls.TailRec def randomInt(): TailRec[Int] = { while (true) { val r = Random.nextInt(100) if (r % 10 != r / 10) { !Return(r) } } throw new AssertionError("Unreachable code"); } val r = randomInt().result r should be < 100 r % 10 should not be r / 10
Examples: -
final
case class
Shift[Domain, Value](continuation: !![Domain, Value]) extends AnyVal with Keyword[Shift[Domain, Value], Value] with Product with Serializable
Author:
杨博 (Yang Bo)
-
final
case class
Using[R <: AutoCloseable](open: () ⇒ R) extends AnyVal with Keyword[Using[R], R] with Product with Serializable
This Using keyword automatically manage resources in scala.concurrent.Future, domains.task.Task, and other asynchrounous domains derived from
Future
orTask
.This Using keyword automatically manage resources in scala.concurrent.Future, domains.task.Task, and other asynchrounous domains derived from
Future
orTask
.Author:
杨博 (Yang Bo)
- final case class WithFilter[UpstreamKeyword, UpstreamValue](upstream: UpstreamKeyword, condition: (UpstreamValue) ⇒ Boolean) extends Keyword[WithFilter[UpstreamKeyword, UpstreamValue], UpstreamValue] with Product with Serializable
-
final
case class
Yield[Element](element: Element) extends AnyVal with Keyword[Yield[Element], Unit] with Product with Serializable
Author:
杨博 (Yang Bo)
This
Yield
keyword must be put inside a function that returnsSeq[Element]
orSeq[Element] !! ...
, or it will not compile."def f(): Int = !Yield(1)" shouldNot compile
, Yield keywords can be used together with other keywords.
def gccFlagBuilder(sourceFile: String, includes: String*): Stream[String] = { !Yield("gcc") !Yield("-c") !Yield(sourceFile) val include = !Each(includes) !Yield("-I") !Yield(include) !Continue } gccFlagBuilder("main.c", "lib1/include", "lib2/include") should be(Stream("gcc", "-c", "main.c", "-I", "lib1/include", "-I", "lib2/include"))
- See also
comprehension if you want to use traditional
for
comprehension instead of !-notation.
Examples: -
final
case class
Catch[Domain, Value](block: !![Domain, Value], catcher: Catcher[!![Domain, Value]]) extends Keyword[Catch[Domain, Value], Value] with Product with Serializable
Author:
杨博 (Yang Bo)
- Annotations
- @deprecated
- Deprecated
(Since version Dsl.scala 1.4.0) keywords.Catch will be removed in favor of Dsl.TryCatch.
Value Members
- object AsynchronousIo
- object Await extends Serializable
-
object
Continue extends Continue with Keyword[Continue, Nothing] with Product with Serializable
A keyword to skip the current iteration in a collection comprehension block.
A keyword to skip the current iteration in a collection comprehension block.
Author:
杨博 (Yang Bo)
Each and Continue can be used to calculate composite numbers and prime numbers.
def compositeNumbersBelow(maxNumber: Int) = collection.immutable.HashSet { val factor = !Each(2 until math.ceil(math.sqrt(maxNumber)).toInt) !Each(2 * factor until maxNumber by factor) } compositeNumbersBelow(13) should be(Set(4, 6, 8, 9, 10, 12)) def primeNumbersBelow(maxNumber: Int) = Seq { val compositeNumbers = compositeNumbersBelow(maxNumber) val i = !Each(2 until maxNumber) if (compositeNumbers(i)) !Continue i } primeNumbersBelow(13) should be(Array(2, 3, 5, 7, 11))
- Note
This Continue keyword is usually used with Each, to skip an element in the loop.
- See also
Each for creating collection comprehensions.
Example: - object Each extends Serializable
- object FlatMap extends Serializable
- object ForEach extends Serializable
- object Fork extends Serializable
- object Get extends Serializable
- object Map extends Serializable
- object Monadic extends Serializable
- object NoneSafe extends Serializable
- object NullSafe extends Serializable
- object Put extends Serializable
- object Return extends Serializable
- object Shift extends LowPriorityShift0 with Serializable
- object Using extends Serializable
- object WithFilter extends Serializable
- object Yield extends LowPriorityYield0 with Serializable
Deprecated Value Members
-
object
Catch extends LowPriorityCatch0 with Serializable
- Annotations
- @deprecated
- Deprecated
(Since version Dsl.scala 1.4.0) keywords.Catch will be removed in favor of Dsl.TryCatch.