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
533 changes: 386 additions & 147 deletions Dsl/src/main/scala/com/thoughtworks/dsl/Dsl.scala

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ final class RaiiSpec extends AnyFreeSpec with Matchers {
@inline
private def jvmCatch[Domain](eh: => Domain !! Throwable)(
failureHandler: Throwable => Domain
)(implicit shiftDsl: Dsl[Shift[Domain, Throwable], Domain, Throwable]): Domain = {
)(implicit shiftDsl: Dsl.Searching[Shift[Domain, Throwable], Domain, Throwable]): Domain = {
val protectedContinuation: Domain !! Throwable =
try {
eh
} catch {
case NonFatal(e) =>
return failureHandler(e)
}
shiftDsl.cpsApply(Shift(protectedContinuation), failureHandler)
shiftDsl(Shift(protectedContinuation), failureHandler)
}

/** Exit the current scope then hang up
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,42 +192,44 @@ object scalaz {
): Dsl.Lift.OneStep[A, F[A]] =
applicative.pure

implicit def scalazMonadTransformerDsl1[F[_[_], _], H[_], G[_], A, B](implicit
given [F[_[_], _], H[_], G[_], A, B](using
monadTrans: MonadTrans[F],
rest: ScalazTransformerDsl[H, G, A, B]
): ScalazTransformerDsl[H, [X] =>> F[G, X], A, B] =
new ScalazTransformerDsl[H, [X] =>> F[G, X], A, B] {
): Dsl.Atomic[Monadic[H[A]], F[G, B], A] =
Dsl.Atomic(new ScalazTransformerDsl[H, [X] =>> F[G, X], A, B] {

def monad: Monad[[X] =>> F[G, X]] = monadTrans(rest.monad)

def lift(fa: H[A]): F[G, A] = monadTrans.liftM(rest.lift(fa))(rest.monad)

}
})

implicit def scalazMonadTransformerDsl0[F[_[_], _], G[_], A, B](implicit
given [F[_[_], _], G[_], A, B](using
monadTrans: MonadTrans[F],
monad0: Monad[G]
): ScalazTransformerDsl[G, [X] =>> F[G, X], A, B] =
new ScalazTransformerDsl[G, [X] =>> F[G, X], A, B] {
): Dsl.Atomic[Monadic[G[A]], F[G, B], A] =
Dsl.Atomic(new ScalazTransformerDsl[G, [X] =>> F[G, X], A, B] {
def monad = monadTrans(monad0)

def lift(fa: G[A]): F[G, A] = monadTrans.liftM(fa)

}
})

implicit def scalazMonadicDsl[F[_], A, B](implicit bind: Bind[F]): Dsl[Monadic[F[A]], F[B], A] =
new Dsl[Monadic[F[A]], F[B], A] {
def cpsApply(keyword: Monadic[F[A]], handler: A => F[B]): F[B] = {
given [F[_], A, B](using
bind: Bind[F]
): Dsl.Atomic[Monadic[F[A]], F[B], A] =
Dsl.Atomic[Monadic[F[A]], F[B], A] {
(keyword: Monadic[F[A]], handler: A => F[B]) =>
bind.bind(Monadic.apply.flip(keyword))(handler)
}
}

abstract class ScalazTransformerDsl[F[_], G[_], A, B] extends Dsl[Monadic[F[A]], G[B], A] {
abstract class ScalazTransformerDsl[F[_], G[_], A, B]
extends ((Monadic[F[A]], A => G[B]) => G[B]) {
def monad: Monad[G]

def lift(fa: F[A]): G[A]

final def cpsApply(keyword: Monadic[F[A]], handler: A => G[B]): G[B] = {
final def apply(keyword: Monadic[F[A]], handler: A => G[B]): G[B] = {
monad.bind(lift(Monadic.apply.flip(keyword)))(handler)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,9 @@ object AsynchronousIo {
}
}

implicit def asynchronousIoDsl[Value]: Dsl[AsynchronousIo[Value], Unit !! Throwable, Value] = { (keyword, handler) =>
keyword.start(_, completionHandler(handler))
}
given [Value]: Dsl.Atomic[AsynchronousIo[Value], Unit !! Throwable, Value] =
Dsl.Atomic { (keyword, handler) =>
keyword.start(_, completionHandler(handler))
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ private trait AwaitJS { this: Await.type =>
given [PromiseResult]
: Dsl.IsKeyword[Await[js.Promise[PromiseResult]], PromiseResult] with {}

given [JsPromiseResult, That]: Dsl[Await[js.Promise[JsPromiseResult]], js.Promise[That], JsPromiseResult] =
Await.apply.liftCo[[X] =>> Dsl[X, js.Promise[That], JsPromiseResult]](_ `then` _)
given [JsPromiseResult, That]: Dsl.Atomic[Await[js.Promise[JsPromiseResult]], js.Promise[That], JsPromiseResult] =
Await.apply.liftCo[[X] =>> Dsl.Atomic[X, js.Promise[That], JsPromiseResult]](Dsl.Atomic(_ `then` _))

given [JsPromiseResult, That](using ExecutionContext): Dsl[Await[js.Promise[JsPromiseResult]], Future[That], JsPromiseResult] =
Await.apply.liftCo[[X] =>> Dsl[X, Future[That], JsPromiseResult]] { (promise, handler) =>
given [JsPromiseResult, That](using ExecutionContext): Dsl.Atomic[Await[js.Promise[JsPromiseResult]], Future[That], JsPromiseResult] =
Await.apply.liftCo[[X] =>> Dsl.Atomic[X, Future[That], JsPromiseResult]](Dsl.Atomic { (promise, handler) =>
promise.toFuture.flatMap(handler)
}
})

extension [FA, A](inline fa: FA)(using
inline notKeyword: util.NotGiven[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,17 +120,17 @@ object Await extends AwaitJS {

given [FutureResult, That](using
ExecutionContext
): Dsl[Await[Future[FutureResult]], Future[That], FutureResult] =
_ flatMap _
): Dsl.Atomic[Await[Future[FutureResult]], Future[That], FutureResult] = Dsl.Atomic(
_ flatMap _)

// // TODO:
// implicit def tailRecContinuationAwaitDsl[Value](implicit
// executionContext: ExecutionContext
// ): Dsl[Await[Value], TailRec[Unit] !! Throwable, Value]
// ): Dsl.Atomic[Await[Value], TailRec[Unit] !! Throwable, Value]

given [Value](using
ExecutionContext
): Dsl[Await[Future[Value]], Unit !! Throwable, Value] = {
): Dsl.Atomic[Await[Future[Value]], Unit !! Throwable, Value] = Dsl.Atomic {
(keyword: Await[Future[Value]], handler: Value => Unit !! Throwable) =>
!!.fromTryContinuation[Unit, Value](keyword.onComplete)(handler)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,14 @@ object Each {
Collection,
Domain
](using
toViewDsl: Dsl.PolyCont[Each.ToView[ForYield], Domain, View[Element]]
): Dsl.PolyCont[
toViewDsl: Dsl.Searching[Each.ToView[ForYield], Domain, View[Element]]
): Dsl.Composed[
Each.To[ForYield, Element, Collection],
Domain,
Collection
] = { case (keyword, handler) =>
] = Dsl.Composed { (keyword, handler) =>
val factory = keyword.factory
toViewDsl.cpsApply(
toViewDsl(
ToView(keyword.forYield),
{ view => handler(view.to(factory)) }
)
Expand Down Expand Up @@ -364,14 +364,14 @@ object Each {
Keyword,
Value
],
polyCont: Dsl.PolyCont[
polyCont: Dsl.Searching[
Keyword,
Domain,
Value
]
): Dsl.PolyCont[Each.ToView[Comprehension], Domain, Value] = {
): Dsl.Composed[Each.ToView[Comprehension], Domain, Value] = Dsl.Composed {
(as, handler) =>
polyCont.cpsApply(toKeyword(as), handler)
polyCont(toKeyword(as), handler)
}
}

Expand Down Expand Up @@ -412,12 +412,12 @@ object Each {
MappedValue
],
factory: Factory[MappedElement, MappedValue],
blockDsl: Dsl.PolyCont[MappedKeyword, Domain, MappedValue]
): Dsl.PolyCont[
blockDsl: Dsl.Searching[MappedKeyword, Domain, MappedValue]
): Dsl.Composed[
FlatMap[Each[Element], MappedKeyword],
Domain,
MappedValue
] = {
] = Dsl.Composed {
case (
FlatMap(sourceCollection, flatMapper: (Element @unchecked => MappedKeyword)),
handler
Expand All @@ -428,7 +428,7 @@ object Each {
): Domain = {
seqOps.headOption match {
case Some(head) =>
blockDsl.cpsApply(
blockDsl(
flatMapper(head),
{ mappedHead =>
loop(
Expand Down Expand Up @@ -456,12 +456,12 @@ object Each {
MappedKeyword,
Domain
](using
blockDsl: Dsl.PolyCont[MappedKeyword, Domain, Unit]
): Dsl.PolyCont[
blockDsl: Dsl.Searching[MappedKeyword, Domain, Unit]
): Dsl.Composed[
FlatMap[Each[Element], MappedKeyword],
Domain,
Unit
] = {
] = Dsl.Composed {
case (
FlatMap(sourceCollection, flatMapper: (Element @unchecked => MappedKeyword)),
handler
Expand All @@ -472,7 +472,7 @@ object Each {
): Domain = {
seqOps.headOption match {
case Some(head) =>
blockDsl.cpsApply(
blockDsl(
flatMapper(head),
{ mappedHead =>
loop(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,23 @@ object FlatMap {
MappedValue,
Domain
](using
upstreamDsl: Dsl.PolyCont[Upstream, Domain, UpstreamValue],
nestedDsl: Dsl.PolyCont[Mapped, Domain, MappedValue]
): Dsl.PolyCont[FlatMap[Upstream, Mapped], Domain, MappedValue] with {
def cpsApply(
upstreamDsl: Dsl.Searching[Upstream, Domain, UpstreamValue],
nestedDsl: Dsl.Searching[Mapped, Domain, MappedValue]
): Dsl.Composed[FlatMap[Upstream, Mapped], Domain, MappedValue] = Dsl.Composed {
(
keyword: FlatMap[Upstream, Mapped],
handler: MappedValue => Domain
): Domain = {
) =>
val FlatMap(upstream, flatMapper) = keyword
upstreamDsl.cpsApply(
upstreamDsl(
upstream,
{ upstreamValue =>
// The typer might erase the type of of parameter of the function
// when the parameter is a reference to a local value, therefore,
// we are unable to call `flatMapper` without a cast.
nestedDsl.cpsApply(flatMapper.asInstanceOf[UpstreamValue => Mapped](upstreamValue), handler)
nestedDsl(flatMapper.asInstanceOf[UpstreamValue => Mapped](upstreamValue), handler)
}
)
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ object Get {
given [S]: IsKeyword[Get[S], S] with {}
def apply[S]: Get[S] = Dsl.Keyword.Opaque.Of(())

implicit def getDsl[S0, S <: S0, A]: Dsl[Get[S0], S => A, S0] = new Dsl[Get[S0], S => A, S0] {
def cpsApply(keyword: Get[S0], handler: S0 => S => A): S => A = { b =>
handler(b)(b)
given [S0, S <: S0, A]: Dsl.Atomic[Get[S0], S => A, S0] =
Dsl.Atomic[Get[S0], S => A, S0] {
(keyword: Get[S0], handler: S0 => S => A) => b =>
handler(b)(b)
}
}

}
41 changes: 25 additions & 16 deletions keywords-If/src/main/scala/com/thoughtworks/dsl/keywords/If.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,37 @@ import Dsl.IsKeyword
import Dsl.cpsApply

final case class If[+ConditionKeyword, +ThenKeyword, +ElseKeyword](
cond: ConditionKeyword,
thenp: ThenKeyword,
elsep: ElseKeyword) extends Dsl.Keyword.Trait
cond: ConditionKeyword,
thenp: ThenKeyword,
elsep: ElseKeyword
) extends Dsl.Keyword.Trait

object If {
given [ConditionKeyword, ThenKeyword, ThenValue, ElseKeyword, ElseValue](using
IsKeyword[ThenKeyword, ThenValue],
IsKeyword[ElseKeyword, ElseValue],
): IsKeyword[If[ConditionKeyword, ThenKeyword, ElseKeyword], ThenValue | ElseValue] with {}
given[ConditionKeyword, ThenKeyword, ElseKeyword, Domain, Value](
using
Dsl.PolyCont[ConditionKeyword, Domain, Boolean],
Dsl.PolyCont[ThenKeyword, Domain, Value],
Dsl.PolyCont[ElseKeyword, Domain, Value],
): Dsl.PolyCont[If[ConditionKeyword, ThenKeyword, ElseKeyword], Domain, Value] with {
def cpsApply(keyword: If[ConditionKeyword, ThenKeyword, ElseKeyword], handler: Value => Domain): Domain = {
keyword.cond.cpsApply{
IsKeyword[ThenKeyword, ThenValue],
IsKeyword[ElseKeyword, ElseValue]
): IsKeyword[
If[ConditionKeyword, ThenKeyword, ElseKeyword],
ThenValue | ElseValue
] with {}
given [ConditionKeyword, ThenKeyword, ElseKeyword, Domain, Value](using
Dsl.Searching[ConditionKeyword, Domain, Boolean],
Dsl.Searching[ThenKeyword, Domain, Value],
Dsl.Searching[ElseKeyword, Domain, Value]
): Dsl.Composed[If[
ConditionKeyword,
ThenKeyword,
ElseKeyword
], Domain, Value] = Dsl.Composed {
(
keyword: If[ConditionKeyword, ThenKeyword, ElseKeyword],
handler: Value => Domain
) =>
keyword.cond.cpsApply {
case true =>
keyword.thenp.cpsApply(handler)
case false =>
keyword.elsep.cpsApply(handler)
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,22 @@ object Match {
opaque type +:[+A, +B] = A | B

object WithIndex:

given [
Index <: Int,
Keyword,
Domain,
LastValue
](using
dsl: Dsl.IsKeyword[Keyword, LastValue],
dsl: Dsl.Searching[Keyword, Domain, LastValue],
valueOfIndex: ValueOf[Index]
): Dsl.IsKeyword[WithIndex[Index, Keyword] +: Nothing, LastValue] with {}
given [
Index <: Int,
Keyword,
Domain,
LastValue
](using
dsl: Dsl.PolyCont[Keyword, Domain, LastValue],
valueOfIndex: ValueOf[Index]
): Dsl.PolyCont[WithIndex[Index, Keyword] +: Nothing, Domain, LastValue] with {
def cpsApply(keywordWithIndex: WithIndex[Index, Keyword] +: Nothing, handler: LastValue => Domain): Domain = {
): Dsl.Composed[WithIndex[Index, Keyword] +: Nothing, Domain, LastValue] = Dsl.Composed {
(keywordWithIndex: WithIndex[Index, Keyword] +: Nothing, handler: LastValue => Domain) =>
keywordWithIndex match {
case WithIndex(valueOfIndex.value, keyword) =>
dsl.cpsApply(keyword, handler)
dsl(keyword, handler)
case _ =>
throw new IllegalArgumentException("Invalid index")
}
}
}

given [
Expand All @@ -46,28 +35,17 @@ object Match {
Domain,
Value
](using
leftDsl: Dsl.IsKeyword[LeftKeyword, Value],
restDsl: Dsl.IsKeyword[RestKeyword, Value]
): Dsl.IsKeyword[WithIndex[Index, LeftKeyword] +: RestKeyword, Value] with {}
given [
Index <: Int,
LeftKeyword,
RestKeyword,
Domain,
Value
](using
leftDsl: Dsl.PolyCont[LeftKeyword, Domain, Value],
leftDsl: Dsl.Searching[LeftKeyword, Domain, Value],
valueOfIndex: ValueOf[Index],
restDsl: Dsl.PolyCont[RestKeyword, Domain, Value]
): Dsl.PolyCont[WithIndex[Index, LeftKeyword] +: RestKeyword, Domain, Value] with {
def cpsApply(keywordUnion: WithIndex[Index, LeftKeyword] +: RestKeyword, handler: Value => Domain): Domain = {
restDsl: Dsl.Searching[RestKeyword, Domain, Value]
): Dsl.Composed[WithIndex[Index, LeftKeyword] +: RestKeyword, Domain, Value] = Dsl.Composed {
(keywordUnion: WithIndex[Index, LeftKeyword] +: RestKeyword, handler: Value => Domain) =>
keywordUnion match {
case WithIndex(valueOfIndex.value, leftKeyword) =>
leftDsl.cpsApply(leftKeyword.asInstanceOf[LeftKeyword], handler)
leftDsl(leftKeyword.asInstanceOf[LeftKeyword], handler)
case _ =>
restDsl.cpsApply(keywordUnion.asInstanceOf[RestKeyword], handler)
restDsl(keywordUnion.asInstanceOf[RestKeyword], handler)
}
}
}

}
Expand Down
Loading