import _root_.scalaz.Trampoline
import _root_.scalaz.Free.Trampoline
import com.thoughtworks.dsl.keywords.Monadic._
import com.thoughtworks.dsl.domains.scalaz._
import com.thoughtworks.dsl.Dsl.reset
val trampoline3 = Trampoline.done(3)
def dslSquare = Trampoline.delay {
s"This string is produced by a trampoline: ${!trampoline3 * !trampoline3}"
}: @reset
dslSquare.run should be("This string is produced by a trampoline: 9")
!trampoline3 is a shortcut of !Monadic(trampoline3),
which will be converted to flatMap calls by our DSL interpreter.
Thus, the method dslSquare is equivalent to the following code in scalaz.syntax:
def scalazSyntaxSquare = trampoline3.flatMap { tmp1 =>
trampoline3.flatMap { tmp2 =>
Trampoline.delay {
s"This string is produced by a trampoline: ${tmp1 * tmp2}"
}
}
}
scalazSyntaxSquare.run should be("This string is produced by a trampoline: 9")
A @reset code block can contain try / catch / finally
if the monadic data type supports scalaz.MonadError.
tryt.scala is a monad transformer that provides
scalaz.MonadError,
therefore try / catch / finally expressions can be used inside a @reset code block
whose return type is TryT[Trampoline, ?].
import com.thoughtworks.tryt.invariant.TryT, TryT._
import scala.util.{Try, Success}
type TryTTransfomredTrampoline[A] = TryT[Trampoline, A]
val trampolineSuccess0: TryTTransfomredTrampoline[Int] = TryT(Trampoline.done(Try(0)))
def dslTryCatch: TryTTransfomredTrampoline[String] = TryT(Trampoline.delay(Try {
try {
s"Division result: ${!trampoline3 / !trampolineSuccess0}"
} catch {
case e: ArithmeticException =>
s"Cannot divide ${!trampoline3} by ${!trampolineSuccess0}"
}
})): @reset
inside(dslTryCatch) {
case TryT(trampoline) =>
trampoline.run should be(Success("Cannot divide 3 by 0"))
}
Note that !-notation can be used on
both trampoline3 and trampolineSuccess0 even when they are different types,
i.e. trampoline3 is a vanilla Trampoline,
while trampolineSuccess0 is a TryT-transfomred
Trampoline.
It is possible because the interpreters of the keywords.Monadic invoke
scalaz.MonadTrans.liftM automatically.
The above dslTryCatch method is equivalent to the following code in scalaz.syntax:
def scalazSyntaxTryCatch: TryTTransfomredTrampoline[String] = {
import _root_.scalaz.syntax.monadError._
trampoline3.liftM[TryT].flatMap { tmp0 =>
trampolineSuccess0.flatMap { tmp1 =>
TryT(Trampoline.delay(Try(s"Division result: ${tmp0 / tmp1}")))
}
}.handleError {
case e: ArithmeticException =>
trampoline3.liftM[TryT].flatMap { tmp2 =>
trampolineSuccess0.flatMap { tmp3 =>
TryT(Trampoline.delay(Try(s"Cannot divide ${tmp2} by ${tmp3}")))
}
}
case e =>
e.raiseError[TryTTransfomredTrampoline, String]
}
}
inside(scalazSyntaxTryCatch) {
case TryT(trampoline) =>
trampoline.run should be(Success("Cannot divide 3 by 0"))
}
Contains interpreters to enable !-notation for Monadic and other keywords in code blocks whose type support scalaz.Bind, scalaz.MonadError and scalaz.MonadTrans.
Author:
杨博 (Yang Bo)
scalaz.Free.Trampoline is a monadic data type that performs tail call optimization. It can be built from a
@reset
code block within some !-notation, similar to the each method in ThoughtWorks Each.!trampoline3
is a shortcut of!Monadic(trampoline3)
, which will be converted toflatMap
calls by our DSL interpreter. Thus, the methoddslSquare
is equivalent to the following code in scalaz.syntax:A
@reset
code block can containtry
/catch
/finally
if the monadic data type supports scalaz.MonadError. tryt.scala is a monad transformer that provides scalaz.MonadError, thereforetry
/catch
/finally
expressions can be used inside a@reset
code block whose return type isTryT[Trampoline, ?]
.Note that !-notation can be used on both
trampoline3
andtrampolineSuccess0
even when they are different types, i.e.trampoline3
is a vanilla Trampoline, whiletrampolineSuccess0
is a TryT-transfomred Trampoline. It is possible because the interpreters of the keywords.Monadic invoke scalaz.MonadTrans.liftM automatically. The abovedslTryCatch
method is equivalent to the following code in scalaz.syntax: