Package

com.thoughtworks.dsl

keywords

Permalink

package keywords

Contains built-in domain-specific Keywords and their corresponding interpreters.

Source
package.scala
Linear Supertypes
Content Hierarchy
Ordering
  1. Alphabetic
  2. By Inheritance
Inherited
  1. keywords
  2. AnyRef
  3. Any
  1. Hide All
  2. Show All
Visibility
  1. Public
  2. All

Type Members

  1. trait AsynchronousIo[Value] extends Keyword[AsynchronousIo[Value], Value]

    Permalink

    The base keyword to perform asynchronous IO in domains.task.Tasks.

    The base keyword to perform asynchronous IO in domains.task.Tasks.

    Example:
    1. The following readAll is a Task to read file content with the help of AsynchronousIo.ReadFile

      import 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
      }

      Tasks created from !-notation can be used in for-comprehension, 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, 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 as a Task[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"
        )
      })
  2. final case class Await[Value](future: Future[Value]) extends AnyVal with Keyword[Await[Value], Value] with Product with Serializable

    Permalink

    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)

    Examples:
    1. 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!"))
    2. ,
    3. !Await can be used together with try / 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!"))
    4. ,
    5. 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)
  3. sealed class Continue extends Keyword[Continue, Nothing]

    Permalink

    The base type of Continue keyword.

    The base type of Continue keyword.

    See also

    The Continue object, which is the only instance of this Continue class.

  4. final case class Each[Element](elements: Traversable[Element]) extends Keyword[Each[Element], Element] with Product with Serializable

    Permalink

    Iterates though each element in elements.

    Iterates though each element in elements.

    Author:

    杨博 (Yang Bo)

    Example:
    1. 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.

  5. 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

    Permalink
  6. final case class ForEach[Element](elements: Traversable[Element]) extends Keyword[ForEach[Element], Element] with Product with Serializable

    Permalink

    Iterates though each element in elements.

  7. final case class Fork[Element](elements: Traversable[Element]) extends AnyVal with Keyword[Fork[Element], Element] with Product with Serializable

    Permalink
  8. final case class Get[S]() extends Keyword[Get[S], S] with Product with Serializable

    Permalink

    Author:

    杨博 (Yang Bo)

    See also

    Put

  9. final case class Map[UpstreamKeyword, UpstreamValue, Value](upstream: UpstreamKeyword, mapper: (UpstreamValue) ⇒ Value) extends Keyword[Map[UpstreamKeyword, UpstreamValue, Value], Value] with Product with Serializable

    Permalink
  10. final case class Monadic[F[_], A](fa: F[A]) extends Keyword[Monadic[F, A], A] with Product with Serializable

    Permalink

    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.scalaz for using this Monadic keyword with scalaz.Monad.

    com.thoughtworks.dsl.domains.cats for using this Monadic keyword with cats.Monad.

  11. final case class NoneSafe[A](option: Option[A]) extends AnyVal with Keyword[NoneSafe[A], A] with Product with Serializable

    Permalink
  12. final case class NullSafe[A <: AnyRef](nullable: A @com.thoughtworks.dsl.Dsl.reset) extends AnyVal with Product with Serializable

    Permalink

    NullSafe is a keyword to perform null check.

    NullSafe is a keyword to perform null check.

    Author:

    杨博 (Yang Bo)

    Examples:
    1. 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)
    2. ,
    3. 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 selecting left, right or value on a null value.

      a[NullPointerException] should be thrownBy {
        root.right.left.right.value
      }

      The above code throws an exception because root.right.left is null. 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 a null 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)
    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.

  13. final case class Put[S](value: S) extends AnyVal with Keyword[Put[S], Unit] with Product with Serializable

    Permalink

    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)

    Examples:
    1. 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")
    2. ,
    3. 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 be O.

      // Output: O
      upperCasedLastCharacter("foo") should be('O')
    See also

    Get

  14. final case class Return[ReturnValue](returnValue: ReturnValue) extends AnyVal with Keyword[Return[ReturnValue], Nothing] with Product with Serializable

    Permalink

    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)

    Examples:
    1. 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
    2. ,
    3. 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
  15. final case class Shift[Domain, Value](continuation: !![Domain, Value]) extends AnyVal with Keyword[Shift[Domain, Value], Value] with Product with Serializable

    Permalink

    Author:

    杨博 (Yang Bo)

  16. final case class Using[R <: AutoCloseable](open: () ⇒ R) extends AnyVal with Keyword[Using[R], R] with Product with Serializable

    Permalink

    This Using keyword automatically manage resources in scala.concurrent.Future, domains.task.Task, and other asynchrounous domains derived from Future or Task.

    This Using keyword automatically manage resources in scala.concurrent.Future, domains.task.Task, and other asynchrounous domains derived from Future or Task.

    Author:

    杨博 (Yang Bo)

    See also

    dsl for usage of this Using keyword in continuations

  17. final case class WithFilter[UpstreamKeyword, UpstreamValue](upstream: UpstreamKeyword, condition: (UpstreamValue) ⇒ Boolean) extends Keyword[WithFilter[UpstreamKeyword, UpstreamValue], UpstreamValue] with Product with Serializable

    Permalink
  18. final case class Yield[Element](element: Element) extends AnyVal with Keyword[Yield[Element], Unit] with Product with Serializable

    Permalink

    Author:

    杨博 (Yang Bo)

    Examples:
    1. 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"))
    2. ,
    3. This Yield keyword must be put inside a function that returns Seq[Element] or Seq[Element] !! ..., or it will not compile.

      "def f(): Int = !Yield(1)" shouldNot compile
    See also

    comprehension if you want to use traditional for comprehension instead of !-notation.

  19. final case class Catch[Domain, Value](block: !![Domain, Value], catcher: Catcher[!![Domain, Value]]) extends Keyword[Catch[Domain, Value], Value] with Product with Serializable

    Permalink

    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

  1. object AsynchronousIo

    Permalink
  2. object Await extends Serializable

    Permalink
  3. object Continue extends Continue with Keyword[Continue, Nothing] with Product with Serializable

    Permalink

    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)

    Example:
    1. 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.

  4. object Each extends Serializable

    Permalink
  5. object FlatMap extends Serializable

    Permalink
  6. object ForEach extends Serializable

    Permalink
  7. object Fork extends Serializable

    Permalink
  8. object Get extends Serializable

    Permalink
  9. object Map extends Serializable

    Permalink
  10. object Monadic extends Serializable

    Permalink
  11. object NoneSafe extends Serializable

    Permalink
  12. object NullSafe extends Serializable

    Permalink
  13. object Put extends Serializable

    Permalink
  14. object Return extends Serializable

    Permalink
  15. object Shift extends LowPriorityShift0 with Serializable

    Permalink
  16. object Using extends Serializable

    Permalink
  17. object WithFilter extends Serializable

    Permalink
  18. object Yield extends LowPriorityYield0 with Serializable

    Permalink

Deprecated Value Members

  1. object Catch extends LowPriorityCatch0 with Serializable

    Permalink
    Annotations
    @deprecated
    Deprecated

    (Since version Dsl.scala 1.4.0) keywords.Catch will be removed in favor of Dsl.TryCatch.

Inherited from AnyRef

Inherited from Any

Ungrouped