diff --git a/Dsl/src/main/scala/com/thoughtworks/dsl/Dsl.scala b/Dsl/src/main/scala/com/thoughtworks/dsl/Dsl.scala index 944925ddf..4fde3abac 100644 --- a/Dsl/src/main/scala/com/thoughtworks/dsl/Dsl.scala +++ b/Dsl/src/main/scala/com/thoughtworks/dsl/Dsl.scala @@ -12,50 +12,67 @@ import scala.util.{Failure, Success, Try} import scala.util.control.{NonFatal, TailCalls} import scala.util.control.TailCalls.TailRec -/** The domain-specific interpreter for `Keyword` in `Domain`, which is a dependent type type class that registers an - * asynchronous callback function, to handle the `Value` inside `Keyword`. +/** The domain-specific interpreter for `Keyword` in `Domain`, which is a + * dependent type type class that registers an asynchronous callback function, + * to handle the `Value` inside `Keyword`. * * @tparam Value * The value held inside `Keyword`. * @author * 杨博 (Yang Bo) * @example - * Creating a collaborative DSL in [[https://github.com/ThoughtWorksInc/Dsl.scala Dsl.scala]] is easy. Only two steps - * are required: + * Creating a collaborative DSL in + * [[https://github.com/ThoughtWorksInc/Dsl.scala Dsl.scala]] is easy. Only + * two steps are required: * - * - Defining their domain-specific [[com.thoughtworks.dsl.Dsl.Keyword Keyword]]. + * - Defining their domain-specific + * [[com.thoughtworks.dsl.Dsl.Keyword Keyword]]. * - Implementing this [[Dsl]] type class, which is an interpreter for an * [[com.thoughtworks.dsl.Dsl.Keyword Keyword]]. */ -@implicitNotFound("The keyword:\n ${Keyword}\nis not supported inside a function that returns:\n${Domain}.") -trait Dsl[-Keyword, Domain, +Value] extends Dsl.PolyCont[Keyword, Domain, Value] +@implicitNotFound( + "The keyword:\n ${Keyword}\nis not supported inside a function that returns:\n${Domain}." +) +opaque type Dsl[-Keyword, Domain, +Value] <: ( + Keyword, + (Value => Domain) +) => Domain = (Keyword, (Value => Domain)) => Domain private[dsl] trait LowPriorityDsl1 { this: Dsl.type => - given deriveFunction1Dsl[Keyword, FunctionDomain, State, Domain, Value](using - isFunctionDomain: FunctionDomain =:= (State => Domain), - restDsl: Dsl[Keyword, Domain, Value] - ): Dsl[Keyword, FunctionDomain, Value] = { - isFunctionDomain.substituteContra[[X] =>> Dsl[Keyword, X, Value]] { - (keyword: Keyword, handler: Value => State => Domain) => - val restDsl1 = restDsl - locally { (state: State) => - val handler1 = handler - restDsl1.cpsApply(keyword, handler1(_)(state)) - } - } + private def deriveFunction1Dsl[Keyword, State, Domain, Value](using + restDsl: Dsl.Searching[Keyword, Domain, Value] + ): Dsl[Keyword, State => Domain, Value] = Dsl { + (keyword: Keyword, handler: Value => State => Domain) => + val restDsl1 = restDsl + locally { (state: State) => + val handler1 = handler + restDsl1(keyword, handler1(_)(state)) + } } + given [Keyword, State, Domain, Value](using + Dsl.IsStackSafe[Domain], + Dsl.Searching[Keyword, Domain, Value] + ): Dsl.Derived.StackSafe[Keyword, State => Domain, Value] = + Dsl.Derived.StackSafe(deriveFunction1Dsl) + + given [Keyword, State, Domain, Value](using + util.NotGiven[Dsl.IsStackSafe[Domain]], + Dsl.Searching[Keyword, Domain, Value] + ): Dsl.Derived.StackUnsafe[Keyword, State => Domain, Value] = + Dsl.Derived.StackUnsafe(deriveFunction1Dsl) + } private[dsl] trait LowPriorityDsl0 extends LowPriorityDsl1 { this: Dsl.type => // // FIXME: Shift // implicit def continuationDsl[Keyword, LeftDomain, RightDomain, Value]( -// implicit restDsl: Dsl[Keyword, LeftDomain, Value], -// shiftDsl2: Dsl[Shift[LeftDomain, RightDomain], LeftDomain, RightDomain] -// ): Dsl[Keyword, LeftDomain !! RightDomain, Value] = { -// new Dsl[Keyword, LeftDomain !! RightDomain, Value] { +// implicit restDsl: Dsl.Atomic[Keyword, LeftDomain, Value], +// shiftDsl2: Dsl.Atomic[Shift[LeftDomain, RightDomain], LeftDomain, RightDomain] +// ): Dsl.Atomic[Keyword, LeftDomain !! RightDomain, Value] = { +// new Dsl.Atomic[Keyword, LeftDomain !! RightDomain, Value] { // def cpsApply(keyword: Keyword, handler: Value => LeftDomain !! RightDomain): LeftDomain !! RightDomain = { // (continue: RightDomain => LeftDomain) => // restDsl.cpsApply(keyword, { a => @@ -65,13 +82,13 @@ private[dsl] trait LowPriorityDsl0 extends LowPriorityDsl1 { this: Dsl.type => // } // } - implicit def throwableContinuationDsl[Keyword, ThrowableContinuationDomain, LeftDomain, Value](implicit - isThrowableContinuationDomain: ThrowableContinuationDomain =:= (LeftDomain !! Throwable), - restDsl: Dsl[Keyword, LeftDomain, Value] - ): Dsl[Keyword, ThrowableContinuationDomain, Value] = - isThrowableContinuationDomain.substituteContra[[X] =>> Dsl[Keyword, X, Value]] { (keyword, handler) => continue => - restDsl.cpsApply( + private def throwableContinuationDsl[Keyword, LeftDomain, Value](implicit + restDsl: Dsl.Searching[Keyword, LeftDomain, Value] + ): Dsl[Keyword, LeftDomain !! Throwable, Value] = Dsl { + (keyword, handler) => continue => + restDsl( keyword, + // Use `new` to support the `return` new (Value => LeftDomain) { def apply(value: Value): LeftDomain = { val protectedContinuation = @@ -86,61 +103,220 @@ private[dsl] trait LowPriorityDsl0 extends LowPriorityDsl1 { this: Dsl.type => } } ) - } + } + given [Keyword, LeftDomain, Value](using + Dsl.IsStackSafe[LeftDomain], + Dsl.Searching[Keyword, LeftDomain, Value] + ): Dsl.Derived.StackSafe[Keyword, LeftDomain !! Throwable, Value] = + Dsl.Derived.StackSafe(throwableContinuationDsl) + given [Keyword, LeftDomain, Value](using + util.NotGiven[Dsl.IsStackSafe[LeftDomain]], + Dsl.Searching[Keyword, LeftDomain, Value] + ): Dsl.Derived.StackUnsafe[Keyword, LeftDomain !! Throwable, Value] = + Dsl.Derived.StackUnsafe(throwableContinuationDsl) } object Dsl extends LowPriorityDsl0 { - - extension [Keyword, Value](inline from: Keyword)(using inline asKeyword: Dsl.IsKeyword[Keyword, Value]) + def apply[Keyword, Domain, Value]: ( + ( + Keyword, + (Value => Domain) + ) => Domain + ) =:= Dsl[Keyword, Domain, Value] = + summon + + trait IsStackSafe[Domain] + object IsStackSafe extends IsStackSafe.LowPriority0: + private[IsStackSafe] trait LowPriority0: + given [R, A]: IsStackSafe[R => A] with {} + given [A]: IsStackSafe[TailRec[A]] with {} + + object Derived: + opaque type StackSafe[-Keyword, Domain, +Value] <: Dsl[ + Keyword, + Domain, + Value + ] = + Dsl[Keyword, Domain, Value] + object StackSafe: + def apply[Keyword, Domain, Value]: ( + ( + Keyword, + (Value => Domain) + ) => Domain + ) =:= StackSafe[Keyword, Domain, Value] = + summon + + opaque type StackUnsafe[-Keyword, Domain, +Value] <: Dsl[ + Keyword, + Domain, + Value + ] = + Dsl[Keyword, Domain, Value] + object StackUnsafe: + def apply[Keyword, Domain, Value]: ( + ( + Keyword, + (Value => Domain) + ) => Domain + ) =:= StackUnsafe[Keyword, Domain, Value] = + summon + + opaque type Composed[-Keyword, Domain, +Value] <: Dsl[ + Keyword, + Domain, + Value + ] = Dsl[Keyword, Domain, Value] + object Composed: + def apply[Keyword, Domain, Value]: ( + ( + Keyword, + (Value => Domain) + ) => Domain + ) =:= Composed[Keyword, Domain, Value] = + summon + opaque type Atomic[-Keyword, Domain, +Value] <: Dsl[Keyword, Domain, Value] = + Dsl[Keyword, Domain, Value] + object Atomic: + def apply[Keyword, Domain, Value]: ( + ( + Keyword, + (Value => Domain) + ) => Domain + ) =:= Atomic[Keyword, Domain, Value] = + summon + + opaque type Searching[-Keyword, Domain, +Value] <: Dsl[ + Keyword, + Domain, + Value + ] = Dsl[Keyword, Domain, Value] + object Searching + extends Searching.AtomicThenStackSafeDerivedThenComposedThenStackUnsafeDerived: + private[Searching] trait StackUnsafeDerived: + given [Keyword, UnsafeDomain, Value](using + dsl: Dsl.Derived.StackUnsafe[Keyword, UnsafeDomain, Value] + ): Dsl.Searching[Keyword, UnsafeDomain, Value] = dsl + private[Searching] trait StackSafeDerivedThenStackUnsafeDerived + extends Searching.StackUnsafeDerived: + given [Keyword, DerivedDomain, Value](using + dsl: Dsl.Derived.StackSafe[Keyword, DerivedDomain, Value] + ): Dsl.Searching[Keyword, DerivedDomain, Value] = dsl + private[Searching] trait ComposedThenStackSafeDerivedThenStackUnsafeDerived + extends Searching.StackSafeDerivedThenStackUnsafeDerived: + given [ComposedKeyword, Domain, Value](using + dsl: Dsl.Composed[ComposedKeyword, Domain, Value] + ): Dsl.Searching[ComposedKeyword, Domain, Value] = dsl + private[Searching] trait AtomicThenComposedThenStackSafeDerivedThenStackUnsafeDerived + extends Searching.ComposedThenStackSafeDerivedThenStackUnsafeDerived: + given [Keyword, Domain, Value](using + dsl: Dsl.Atomic[Keyword, Domain, Value] + ): Dsl.Searching[Keyword, Domain, Value] = dsl + object AtomicThenComposedThenStackSafeDerivedThenStackUnsafeDerived + extends AtomicThenComposedThenStackSafeDerivedThenStackUnsafeDerived + + private[Searching] trait ComposedThenStackUnsafeDerived + extends Searching.StackUnsafeDerived: + given [ComposedKeyword, Domain, Value](using + dsl: Dsl.Composed[ComposedKeyword, Domain, Value] + ): Dsl.Searching[ComposedKeyword, Domain, Value] = dsl + private[Searching] trait StackSafeDerivedThenComposedThenStackUnsafeDerived + extends Searching.ComposedThenStackUnsafeDerived: + given [Keyword, DerivedDomain, Value](using + dsl: Dsl.Derived.StackSafe[Keyword, DerivedDomain, Value] + ): Dsl.Searching[Keyword, DerivedDomain, Value] = dsl + private[Searching] trait AtomicThenStackSafeDerivedThenComposedThenStackUnsafeDerived + extends Searching.StackSafeDerivedThenComposedThenStackUnsafeDerived: + given [Keyword, Domain, Value](using + dsl: Dsl.Atomic[Keyword, Domain, Value] + ): Dsl.Searching[Keyword, Domain, Value] = dsl + object AtomicThenStackSafeDerivedThenComposedThenStackUnsafeDerived + extends AtomicThenStackSafeDerivedThenComposedThenStackUnsafeDerived + + extension [Keyword, Value]( + inline from: Keyword + )(using inline asKeyword: Dsl.IsKeyword[Keyword, Value]) transparent inline def unary_! : Value = { Dsl.shift[Keyword, Value](from) } sealed trait HasValueOrElement[KeywordOrView, Element]: extension (keywordOrView: KeywordOrView) - 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 + 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 object HasValueOrElement { - given [KeywordOrView <: For.Yield[Element], Element]: HasValueOrElement[KeywordOrView, Element] with {} + given [KeywordOrView <: For.Yield[Element], Element] + : HasValueOrElement[KeywordOrView, Element] with {} } /** The AST returned from a `for`...`yield` or a `for`...`do` expression. * - * Note that a [[For]] does not directly support !-notation. - * Instead, [[keywords.Each.ToView]] is used to convert a [[For]] to a - * [[Keyword]] that supports !-notation. + * Note that a [[For]] does not directly support !-notation. Instead, + * [[keywords.Each.ToView]] is used to convert a [[For]] to a [[Keyword]] + * that supports !-notation. */ 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 + 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 } + /** 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] + 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] } } - implicit def derivedTailRecDsl[Keyword, TailRecDomain, Domain, Value](implicit - isTailRecDomain: TailRecDomain =:= TailRec[Domain], - restDsl: Dsl[Keyword, Domain, Value] - ): Dsl[Keyword, TailRecDomain, Value] = isTailRecDomain.substituteContra[[X] =>> Dsl[Keyword, X, Value]] { - (keyword, handler) => + private def derivedTailRecDsl[Keyword, Domain, Value](implicit + restDsl: Dsl.Searching[Keyword, Domain, Value] + ): Dsl[Keyword, TailRec[Domain], Value] = Dsl { + (keyword: Keyword, handler: (Value => TailRec[Domain])) => TailCalls.done { - restDsl.cpsApply( + restDsl( keyword, { value => handler(value).result @@ -148,32 +324,54 @@ object Dsl extends LowPriorityDsl0 { ) } } - - implicit def derivedThrowableTailRecDsl[Keyword, TaskDomain, LeftDomain, Value](implicit - isTaskDomain: TaskDomain =:= (TailRec[LeftDomain] !! Throwable), - restDsl: Dsl[Keyword, LeftDomain !! Throwable, Value] - ): Dsl[Keyword, TaskDomain, Value] = isTaskDomain.substituteContra[[X] =>> Dsl[Keyword, X, Value]] { - (keyword, handler) => tailRecFailureHandler => - TailCalls.done( - restDsl.cpsApply( - keyword, - { value => failureHandler => - handler(value) { e => - TailCalls.done(failureHandler(e)) - }.result + given [Keyword, Domain, Value](using + Dsl.IsStackSafe[Domain], + Dsl.Searching[Keyword, Domain, Value] + ): Dsl.Derived.StackSafe[Keyword, TailRec[Domain], Value] = + Dsl.Derived.StackSafe(derivedTailRecDsl) + given [Keyword, Domain, Value](using + util.NotGiven[Dsl.IsStackSafe[Domain]], + Dsl.Searching[Keyword, Domain, Value] + ): Dsl.Derived.StackUnsafe[Keyword, TailRec[Domain], Value] = + Dsl.Derived.StackUnsafe(derivedTailRecDsl) + + private def derivedThrowableTailRecDsl[Keyword, LeftDomain, Value](implicit + restDsl: Dsl.Searching[Keyword, LeftDomain !! Throwable, Value] + ): Dsl[Keyword, TailRec[LeftDomain] !! Throwable, Value] = + Dsl { + ( + keyword: Keyword, + handler: (Value => TailRec[LeftDomain] !! Throwable) + ) => (tailRecFailureHandler: Throwable => TailRec[LeftDomain]) => + TailCalls.done( + restDsl( + keyword, + { value => failureHandler => + handler(value) { e => + TailCalls.done(failureHandler(e)) + }.result + } + ) { e => + tailRecFailureHandler(e).result } - ) { e => - tailRecFailureHandler(e).result - } - ) - } + ) + } + given [Keyword, LeftDomain, TailRecValue](using + Dsl.IsStackSafe[LeftDomain], + Dsl.Searching[Keyword, LeftDomain !! Throwable, TailRecValue] + ): Dsl.Derived.StackSafe[Keyword, TailRec[LeftDomain] !! Throwable, TailRecValue] = + Dsl.Derived.StackSafe(derivedThrowableTailRecDsl) + given [Keyword, LeftDomain, TailRecValue](using + util.NotGiven[Dsl.IsStackSafe[LeftDomain]], + Dsl.Searching[Keyword, LeftDomain !! Throwable, TailRecValue] + ): Dsl.Derived.StackUnsafe[Keyword, TailRec[LeftDomain] !! Throwable, TailRecValue] = + Dsl.Derived.StackUnsafe(derivedThrowableTailRecDsl) private[dsl] type !![R, +A] = (A => R) => R - def apply[Keyword, Domain, Value](implicit typeClass: Dsl[Keyword, Domain, Value]): Dsl[Keyword, Domain, Value] = - typeClass - - private def catchNativeException[A](futureContinuation: Future[A] !! A): Future[A] = { + private def catchNativeException[A]( + futureContinuation: Future[A] !! A + ): Future[A] = { try { futureContinuation(Future.successful) } catch { @@ -182,10 +380,12 @@ object Dsl extends LowPriorityDsl0 { } } - /** The type class to support `try` ... `catch` ... `finally` expression for `OutputDomain`. + /** The type class to support `try` ... `catch` ... `finally` expression for + * `OutputDomain`. * - * !-notation is allowed by default for `? !! Throwable` and [[scala.concurrent.Future Future]] domains, with the - * help of this type class. + * !-notation is allowed by default for `? !! Throwable` and + * [[scala.concurrent.Future Future]] domains, with the help of this type + * class. */ @implicitNotFound( "The `try` ... `catch` ... `finally` expression cannot contain !-notation inside a function that returns ${OuterDomain}." @@ -201,8 +401,18 @@ object Dsl extends LowPriorityDsl0 { object TryCatchFinally { - implicit def fromTryCatchTryFinally[Value, OuterDomain, BlockDomain, FinalizerDomain](implicit - tryFinally: TryFinally[Value, OuterDomain, BlockDomain, FinalizerDomain], + implicit def fromTryCatchTryFinally[ + Value, + OuterDomain, + BlockDomain, + FinalizerDomain + ](implicit + tryFinally: TryFinally[ + Value, + OuterDomain, + BlockDomain, + FinalizerDomain + ], tryCatch: TryCatch[Value, BlockDomain, BlockDomain] ): TryCatchFinally[Value, OuterDomain, BlockDomain, FinalizerDomain] = { ( @@ -233,21 +443,26 @@ object Dsl extends LowPriorityDsl0 { } private[dsl] trait LowPriorityTryCatch { - implicit def liftFunction1TryCatch[Value, OuterDomain, BlockDomain, State](implicit - restTryCatch: TryCatch[Value, OuterDomain, BlockDomain] + implicit def liftFunction1TryCatch[Value, OuterDomain, BlockDomain, State]( + implicit restTryCatch: TryCatch[Value, OuterDomain, BlockDomain] ): TryCatch[Value, State => OuterDomain, State => BlockDomain] = { ( block: (State => BlockDomain) !! Value, catcher: Catcher[(State => BlockDomain) !! Value], outerSuccessHandler: Value => State => OuterDomain ) => (state: State) => - def withState(blockContinuation: (State => BlockDomain) !! Value) = { (blockHandler: (Value => BlockDomain)) => - blockContinuation { (value: Value) => (state: State) => - blockHandler(value) - }(state) + def withState(blockContinuation: (State => BlockDomain) !! Value) = { + (blockHandler: (Value => BlockDomain)) => + blockContinuation { (value: Value) => (state: State) => + blockHandler(value) + }(state) } - restTryCatch.tryCatch(withState(block), catcher.andThen(withState _), outerSuccessHandler(_)(state)) + restTryCatch.tryCatch( + withState(block), + catcher.andThen(withState _), + outerSuccessHandler(_)(state) + ) } } @@ -348,22 +563,43 @@ object Dsl extends LowPriorityDsl0 { } private[dsl] trait LowPriorityTryFinally { - implicit def liftFunction1TryCatch[Value, OuterDomain, BlockDomain, FinalizerDomain, State](implicit - restTryFinally: TryFinally[Value, OuterDomain, BlockDomain, FinalizerDomain] - ): TryFinally[Value, State => OuterDomain, State => BlockDomain, State => FinalizerDomain] = { + implicit def liftFunction1TryCatch[ + Value, + OuterDomain, + BlockDomain, + FinalizerDomain, + State + ](implicit + restTryFinally: TryFinally[ + Value, + OuterDomain, + BlockDomain, + FinalizerDomain + ] + ): TryFinally[ + Value, + State => OuterDomain, + State => BlockDomain, + State => FinalizerDomain + ] = { ( block: (State => BlockDomain) !! Value, finalizer: (State => FinalizerDomain) !! Unit, outerSuccessHandler: Value => State => OuterDomain ) => state => - def withState[Domain, Value](blockContinuation: (State => Domain) !! Value) = { - (blockHandler: (Value => Domain)) => - blockContinuation { (value: Value) => (state: State) => - blockHandler(value) - }(state) + def withState[Domain, Value]( + blockContinuation: (State => Domain) !! Value + ) = { (blockHandler: (Value => Domain)) => + blockContinuation { (value: Value) => (state: State) => + blockHandler(value) + }(state) } - restTryFinally.tryFinally(withState(block), withState(finalizer), outerSuccessHandler(_)(state)) + restTryFinally.tryFinally( + withState(block), + withState(finalizer), + outerSuccessHandler(_)(state) + ) } } @@ -371,7 +607,9 @@ object Dsl extends LowPriorityDsl0 { implicit def futureTryFinally[BlockValue, OuterValue](implicit executionContext: ExecutionContext - ): TryFinally[BlockValue, Future[OuterValue], Future[BlockValue], Future[Unit]] = { + ): TryFinally[BlockValue, Future[OuterValue], Future[BlockValue], Future[ + Unit + ]] = { ( block: Future[BlockValue] !! BlockValue, finalizer: Future[Unit] !! Unit, @@ -395,40 +633,45 @@ object Dsl extends LowPriorityDsl0 { } } - implicit def throwableContinuationTryFinally[LeftDomain, Value] - : TryFinally[Value, LeftDomain !! Throwable, LeftDomain !! Throwable, LeftDomain !! Throwable] = { - (block, finalizer, outerSuccessHandler) => outerFailureHandler => - @inline - def injectFinalizer(finalizerHandler: Unit => LeftDomain !! Throwable): LeftDomain = { - locally { - try { - finalizer(finalizerHandler) - } catch { - case NonFatal(e) => - return outerFailureHandler(e) - } - }(outerFailureHandler) - } - - @inline - def hookedFailureHandler(e: Throwable) = - injectFinalizer { (_: Unit) => - _(e) - } - - def runBlock(): LeftDomain = { - (try { - block { value => hookedFailureHandler => - injectFinalizer { (_: Unit) => - outerSuccessHandler(value) - } - } + implicit def throwableContinuationTryFinally[LeftDomain, Value]: TryFinally[ + Value, + LeftDomain !! Throwable, + LeftDomain !! Throwable, + LeftDomain !! Throwable + ] = { (block, finalizer, outerSuccessHandler) => outerFailureHandler => + @inline + def injectFinalizer( + finalizerHandler: Unit => LeftDomain !! Throwable + ): LeftDomain = { + locally { + try { + finalizer(finalizerHandler) } catch { case NonFatal(e) => - return hookedFailureHandler(e) - })(hookedFailureHandler) + return outerFailureHandler(e) + } + }(outerFailureHandler) + } + + @inline + def hookedFailureHandler(e: Throwable) = + injectFinalizer { (_: Unit) => + _(e) } - runBlock() + + def runBlock(): LeftDomain = { + (try { + block { value => hookedFailureHandler => + injectFinalizer { (_: Unit) => + outerSuccessHandler(value) + } + } + } catch { + case NonFatal(e) => + return hookedFailureHandler(e) + })(hookedFailureHandler) + } + runBlock() } } @@ -464,14 +707,16 @@ object Dsl extends LowPriorityDsl0 { } } - private[Lift] trait LowPriorityOneStep0 extends LowPriorityOneStep1 { this: OneStep.type => + private[Lift] trait LowPriorityOneStep0 extends LowPriorityOneStep1 { + this: OneStep.type => given [R, F, A]: OneStep[R, A => R] = { r => Function.const(r) } } object OneStep extends LowPriorityOneStep0 { import Dsl.!! - given [LeftDomain, RightDomain]: OneStep[RightDomain, LeftDomain !! RightDomain] = r => _(r) + given [LeftDomain, RightDomain] + : OneStep[RightDomain, LeftDomain !! RightDomain] = r => _(r) given [Element](using ExecutionContext @@ -482,27 +727,21 @@ object Dsl extends LowPriorityDsl0 { } - trait PolyCont[-Keyword, Domain, +Value] { - - /** Registers an asynchronous callback `handler` on `keyword`, to handle the `Value`. */ - def cpsApply(keyword: Keyword, handler: Value => Domain): Domain - - } - opaque type Run[Keyword, Domain, Value] <: Keyword => Domain = Keyword => Domain object Run { given [Keyword, Domain, Value](using - dsl: /*=>*/ PolyCont[Keyword, Domain, Value], + dsl: /*=>*/ Dsl.Searching[Keyword, Domain, Value], lift: /*=>*/ Lift[Value, Domain] - ): Run[Keyword, Domain, Value] = { dsl.cpsApply(_, lift) } + ): Run[Keyword, Domain, Value] = { dsl.apply(_, lift) } } type Keyword = Keyword.Opaque | Keyword.Trait object Keyword { + /** A marker trait that denotes a keyword class, enabling extension method * defined in [[Dsl]] for subclasses of [[Keyword.Trait]]. */ @@ -519,8 +758,7 @@ object Dsl extends LowPriorityDsl0 { } } } - - + trait IsKeyword[Keyword, Value] extends HasValueOrElement[Keyword, Value]: extension (keyword: Keyword) @inline def to[Domain[_]](using @@ -535,15 +773,16 @@ object Dsl extends LowPriorityDsl0 { run(keyword) } - extension [Keyword, Domain, Value](keyword: Keyword) @inline def cpsApply(using - dsl: PolyCont[Keyword, Domain, Value] + dsl: Dsl.Searching[Keyword, Domain, Value] )(handler: Value => Domain)(using DummyImplicit): Domain = { - dsl.cpsApply(keyword, handler) + dsl(keyword, handler) } - @annotation.compileTimeOnly("""This method must be called only inside a `reset` or `*` code block.""") + @annotation.compileTimeOnly( + """This method must be called only inside a `reset` or `*` code block.""" + ) def shift[Keyword, Value](keyword: Keyword): Value = ??? } diff --git a/domains-Task/.jvm/src/test/scala/com/thoughtworks/dsl/domains/RaiiSpec.scala b/domains-Task/.jvm/src/test/scala/com/thoughtworks/dsl/domains/RaiiSpec.scala index 405895728..ada8f8072 100644 --- a/domains-Task/.jvm/src/test/scala/com/thoughtworks/dsl/domains/RaiiSpec.scala +++ b/domains-Task/.jvm/src/test/scala/com/thoughtworks/dsl/domains/RaiiSpec.scala @@ -16,7 +16,7 @@ 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 @@ -24,7 +24,7 @@ final class RaiiSpec extends AnyFreeSpec with Matchers { case NonFatal(e) => return failureHandler(e) } - shiftDsl.cpsApply(Shift(protectedContinuation), failureHandler) + shiftDsl(Shift(protectedContinuation), failureHandler) } /** Exit the current scope then hang up diff --git a/domains-scalaz/src/main/scala/com/thoughtworks/dsl/domains/scalaz.scala b/domains-scalaz/src/main/scala/com/thoughtworks/dsl/domains/scalaz.scala index aeb9aa8b8..23947e8ae 100644 --- a/domains-scalaz/src/main/scala/com/thoughtworks/dsl/domains/scalaz.scala +++ b/domains-scalaz/src/main/scala/com/thoughtworks/dsl/domains/scalaz.scala @@ -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) } diff --git a/keywords-AsynchronousIo/src/main/scala/com/thoughtworks/dsl/keywords/AsynchronousIo.scala b/keywords-AsynchronousIo/src/main/scala/com/thoughtworks/dsl/keywords/AsynchronousIo.scala index 44b3c3d44..1a9d1e249 100644 --- a/keywords-AsynchronousIo/src/main/scala/com/thoughtworks/dsl/keywords/AsynchronousIo.scala +++ b/keywords-AsynchronousIo/src/main/scala/com/thoughtworks/dsl/keywords/AsynchronousIo.scala @@ -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)) + } } diff --git a/keywords-Await/.js/src/main/scala/com/thoughtworks/dsl/keywords/AwaitJS.scala b/keywords-Await/.js/src/main/scala/com/thoughtworks/dsl/keywords/AwaitJS.scala index d8431cd8b..c9c8fc455 100644 --- a/keywords-Await/.js/src/main/scala/com/thoughtworks/dsl/keywords/AwaitJS.scala +++ b/keywords-Await/.js/src/main/scala/com/thoughtworks/dsl/keywords/AwaitJS.scala @@ -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[ diff --git a/keywords-Await/src/main/scala/com/thoughtworks/dsl/keywords/Await.scala b/keywords-Await/src/main/scala/com/thoughtworks/dsl/keywords/Await.scala index d54816d0d..a2d61ac3f 100644 --- a/keywords-Await/src/main/scala/com/thoughtworks/dsl/keywords/Await.scala +++ b/keywords-Await/src/main/scala/com/thoughtworks/dsl/keywords/Await.scala @@ -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) } diff --git a/keywords-Each/src/main/scala/com/thoughtworks/dsl/keywords/Each.scala b/keywords-Each/src/main/scala/com/thoughtworks/dsl/keywords/Each.scala index 52791fce0..a184885c3 100644 --- a/keywords-Each/src/main/scala/com/thoughtworks/dsl/keywords/Each.scala +++ b/keywords-Each/src/main/scala/com/thoughtworks/dsl/keywords/Each.scala @@ -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)) } ) @@ -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) } } @@ -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 @@ -428,7 +428,7 @@ object Each { ): Domain = { seqOps.headOption match { case Some(head) => - blockDsl.cpsApply( + blockDsl( flatMapper(head), { mappedHead => loop( @@ -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 @@ -472,7 +472,7 @@ object Each { ): Domain = { seqOps.headOption match { case Some(head) => - blockDsl.cpsApply( + blockDsl( flatMapper(head), { mappedHead => loop( diff --git a/keywords-FlatMap/src/main/scala/com/thoughtworks/dsl/keywords/FlatMap.scala b/keywords-FlatMap/src/main/scala/com/thoughtworks/dsl/keywords/FlatMap.scala index bf3f284c7..178859968 100644 --- a/keywords-FlatMap/src/main/scala/com/thoughtworks/dsl/keywords/FlatMap.scala +++ b/keywords-FlatMap/src/main/scala/com/thoughtworks/dsl/keywords/FlatMap.scala @@ -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) } ) - } + } } diff --git a/keywords-Get/src/main/scala/com/thoughtworks/dsl/keywords/Get.scala b/keywords-Get/src/main/scala/com/thoughtworks/dsl/keywords/Get.scala index 068c2f026..8a406892f 100644 --- a/keywords-Get/src/main/scala/com/thoughtworks/dsl/keywords/Get.scala +++ b/keywords-Get/src/main/scala/com/thoughtworks/dsl/keywords/Get.scala @@ -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) } - } } diff --git a/keywords-If/src/main/scala/com/thoughtworks/dsl/keywords/If.scala b/keywords-If/src/main/scala/com/thoughtworks/dsl/keywords/If.scala index c7e721427..54246c64b 100644 --- a/keywords-If/src/main/scala/com/thoughtworks/dsl/keywords/If.scala +++ b/keywords-If/src/main/scala/com/thoughtworks/dsl/keywords/If.scala @@ -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) } - } } -} \ No newline at end of file +} diff --git a/keywords-Match/src/main/scala/com/thoughtworks/dsl/keywords/Match.scala b/keywords-Match/src/main/scala/com/thoughtworks/dsl/keywords/Match.scala index a7e50dea6..a0fe4c27f 100644 --- a/keywords-Match/src/main/scala/com/thoughtworks/dsl/keywords/Match.scala +++ b/keywords-Match/src/main/scala/com/thoughtworks/dsl/keywords/Match.scala @@ -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 [ @@ -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) } - } } } diff --git a/keywords-NoneSafe/src/main/scala/com/thoughtworks/dsl/keywords/NoneSafe.scala b/keywords-NoneSafe/src/main/scala/com/thoughtworks/dsl/keywords/NoneSafe.scala index f681fee11..82e9be8f0 100644 --- a/keywords-NoneSafe/src/main/scala/com/thoughtworks/dsl/keywords/NoneSafe.scala +++ b/keywords-NoneSafe/src/main/scala/com/thoughtworks/dsl/keywords/NoneSafe.scala @@ -3,23 +3,24 @@ package keywords import com.thoughtworks.dsl.Dsl.IsKeyword import scala.language.implicitConversions -opaque type NoneSafe[+A] <: Dsl.Keyword.Opaque = Dsl.Keyword.Opaque.Of[Option[A]] +opaque type NoneSafe[+A] <: Dsl.Keyword.Opaque = + Dsl.Keyword.Opaque.Of[Option[A]] object NoneSafe { given [A]: IsKeyword[NoneSafe[A], A] with {} def apply[A]: Option[A] =:= NoneSafe[A] = Dsl.Keyword.Opaque.Of.apply - implicit def noneSafeDsl[A, Domain](implicit - continueDsl: Dsl[Return[None.type], Domain, Nothing] - ): Dsl[NoneSafe[A], Domain, A] = { + given [A, Domain](using + continueDsl: Dsl.Searching[Return[None.type], Domain, Nothing] + ): Dsl.Atomic[NoneSafe[A], Domain, A] = Dsl.Atomic[Option[A], Domain, A] { (keyword: Option[A], handler: A => Domain) => keyword match { case None => - continueDsl.cpsApply(Return(None), identity) + continueDsl(Return(None), identity) case Some(a) => handler(a) } - }: Dsl[Option[A], Domain, A] + } extension [FA, A](inline fa: FA)(using inline notKeyword: util.NotGiven[ diff --git a/keywords-Pure/src/main/scala/com/thoughtworks/dsl/keywords/Pure.scala b/keywords-Pure/src/main/scala/com/thoughtworks/dsl/keywords/Pure.scala index cdccae70e..22424ca9c 100644 --- a/keywords-Pure/src/main/scala/com/thoughtworks/dsl/keywords/Pure.scala +++ b/keywords-Pure/src/main/scala/com/thoughtworks/dsl/keywords/Pure.scala @@ -6,9 +6,9 @@ import Dsl.IsKeyword opaque type Pure[+Value] <: Dsl.Keyword.Opaque = Dsl.Keyword.Opaque.Of[Value] object Pure { given [Domain, Value](using - shiftDsl: Dsl.PolyCont[Shift[Domain, Value], Domain, Value] - ): Dsl[Pure[Value], Domain, Value] = { (keyword: Pure[Value], handler: Value => Domain) => - shiftDsl.cpsApply(Shift(_(keyword)), handler) + shiftDsl: Dsl.Searching[Shift[Domain, Value], Domain, Value] + ): Dsl.Atomic[Pure[Value], Domain, Value] = Dsl.Atomic { (keyword: Pure[Value], handler: Value => Domain) => + shiftDsl(Shift(_(keyword)), handler) } given [PureValue]: IsKeyword[Pure[PureValue], PureValue] with {} diff --git a/keywords-Put/src/main/scala/com/thoughtworks/dsl/keywords/Put.scala b/keywords-Put/src/main/scala/com/thoughtworks/dsl/keywords/Put.scala index 2f674e376..ddc517c5f 100644 --- a/keywords-Put/src/main/scala/com/thoughtworks/dsl/keywords/Put.scala +++ b/keywords-Put/src/main/scala/com/thoughtworks/dsl/keywords/Put.scala @@ -67,11 +67,9 @@ object Put { given [S]: IsKeyword[Put[S], Unit] with {} def apply[S]: S =:= Put[S] = Dsl.Keyword.Opaque.Of.apply - implicit def putDsl[S0, S >: S0, A]: Dsl[Put[S0], S => A, Unit] = - new Dsl[Put[S0], S => A, Unit] { - def cpsApply(keyword: Put[S0], handler: Unit => S => A): S => A = { - oldValue => - handler(())(keyword) - } + given [S0, S >: S0, A]: Dsl.Atomic[Put[S0], S => A, Unit] = + Dsl.Atomic[Put[S0], S => A, Unit] { + (keyword: Put[S0], handler: Unit => S => A) => oldValue => + handler(())(keyword) } } diff --git a/keywords-Return/src/main/scala/com/thoughtworks/dsl/keywords/Return.scala b/keywords-Return/src/main/scala/com/thoughtworks/dsl/keywords/Return.scala index 5a5736b9d..a985c097b 100644 --- a/keywords-Return/src/main/scala/com/thoughtworks/dsl/keywords/Return.scala +++ b/keywords-Return/src/main/scala/com/thoughtworks/dsl/keywords/Return.scala @@ -8,29 +8,32 @@ import scala.language.implicitConversions import Dsl.IsKeyword import Dsl.Lift -/** 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) */ -opaque type Return[+ReturnValue] <: Dsl.Keyword.Opaque = Dsl.Keyword.Opaque.Of[ReturnValue] +opaque type Return[+ReturnValue] <: Dsl.Keyword.Opaque = + Dsl.Keyword.Opaque.Of[ReturnValue] object Return { - @inline def apply[ReturnValue]: ReturnValue =:= Return[ReturnValue] = Dsl.Keyword.Opaque.Of.apply + @inline def apply[ReturnValue]: ReturnValue =:= Return[ReturnValue] = + Dsl.Keyword.Opaque.Of.apply given [ReturnValue]: IsKeyword[Return[ReturnValue], Nothing] with {} given [ReturnValue, Domain](using lift: Lift[ReturnValue, Domain] - ): Dsl[Return[ReturnValue], Domain, Nothing] with { - def cpsApply(keyword: Return[ReturnValue], handler: Nothing => Domain): Domain = { + ): Dsl.Atomic[Return[ReturnValue], Domain, Nothing] = Dsl.Atomic { + (keyword: Return[ReturnValue], handler: Nothing => Domain) => lift(keyword) - } + } - implicit def returnDsl[ReturnValue, Domain >: ReturnValue]: Dsl[Return[ReturnValue], Domain, Nothing] = - new Dsl[Return[ReturnValue], Domain, Nothing] { - def cpsApply(keyword: Return[ReturnValue], handler: Nothing => Domain): Domain = { + given [ReturnValue, Domain >: ReturnValue] + : Dsl.Atomic[Return[ReturnValue], Domain, Nothing] = + Dsl.Atomic[Return[ReturnValue], Domain, Nothing] { + (keyword: Return[ReturnValue], handler: Nothing => Domain) => keyword - } } } diff --git a/keywords-Shift/src/main/scala/com/thoughtworks/dsl/keywords/Shift.scala b/keywords-Shift/src/main/scala/com/thoughtworks/dsl/keywords/Shift.scala index 7f6cc9dc6..4a51b6c52 100644 --- a/keywords-Shift/src/main/scala/com/thoughtworks/dsl/keywords/Shift.scala +++ b/keywords-Shift/src/main/scala/com/thoughtworks/dsl/keywords/Shift.scala @@ -4,7 +4,10 @@ import Dsl.IsKeyword import com.thoughtworks.dsl.Dsl import com.thoughtworks.dsl.Dsl.!! -import com.thoughtworks.dsl.keywords.Shift.{SameDomainStackSafeShiftDsl, StackSafeShiftDsl} +import com.thoughtworks.dsl.keywords.Shift.{ + SameDomainStackSafeShiftDsl, + StackSafeShiftDsl +} import scala.annotation.tailrec import scala.language.implicitConversions @@ -19,35 +22,54 @@ opaque type Shift[R, A] <: Dsl.Keyword.Opaque = Dsl.Keyword.Opaque.Of[R !! A] private[keywords] trait LowPriorityShift1 { @inline - implicit def stackUnsafeShiftDsl[Domain, Value]: Dsl[Shift[Domain, Value], Domain, Value] = - new Dsl[Shift[Domain, Value], Domain, Value] { - def cpsApply(shift: Shift[Domain, Value], handler: Value => Domain) = + implicit def stackUnsafeShiftDsl[Domain, Value] + : Dsl.Atomic[Shift[Domain, Value], Domain, Value] = + Dsl.Atomic[Shift[Domain, Value], Domain, Value] { + (shift: Shift[Domain, Value], handler: Value => Domain) => Shift.apply.flip(shift)(handler) } } -private[keywords] trait LowPriorityShift0 extends LowPriorityShift1 { this: Shift.type => +private[keywords] trait LowPriorityShift0 extends LowPriorityShift1 { + this: Shift.type => given [LeftDomain, RightDomain, Value](using restDsl: SameDomainStackSafeShiftDsl[LeftDomain, RightDomain] - ): SameDomainStackSafeShiftDsl[LeftDomain !! RightDomain, Value] = { - val restDsl0 = apply.liftContra[[X] =>> Dsl[X, LeftDomain, RightDomain]](restDsl); - { (keyword, handler) => - keyword { value => - restDsl0.cpsApply(handler(value), _) + ): SameDomainStackSafeShiftDsl[LeftDomain !! RightDomain, Value] = + SameDomainStackSafeShiftDsl[LeftDomain !! RightDomain, Value] { + val restDsl0 = + apply + .liftContra[[X] =>> Dsl.Atomic[X, LeftDomain, RightDomain]](restDsl); + { (keyword, handler) => + keyword { value => + restDsl0(handler(value), _) + } } } - } } object Shift extends LowPriorityShift0 { given [Domain, Value]: IsKeyword[Shift[Domain, Value], Value] with {} - trait StackSafeShiftDsl[Domain, NewDomain, Value] extends Dsl[Shift[Domain, Value], NewDomain, Value] - - private[keywords] type SameDomainStackSafeShiftDsl[Domain, Value] = StackSafeShiftDsl[Domain, Domain, Value] + opaque type StackSafeShiftDsl[Domain, NewDomain, Value] <: Dsl.Atomic[Shift[ + Domain, + Value + ], NewDomain, Value] = Dsl.Atomic[Shift[Domain, Value], NewDomain, Value] + object StackSafeShiftDsl: + def apply[Domain, NewDomain, Value]: ( + ( + Shift[Domain, Value], + Value => NewDomain + ) => NewDomain + ) =:= StackSafeShiftDsl[Domain, NewDomain, Value] = + Dsl.Atomic.apply[Shift[Domain, Value], NewDomain, Value] + + private[keywords] type SameDomainStackSafeShiftDsl[Domain, Value] = + StackSafeShiftDsl[Domain, Domain, Value] + object SameDomainStackSafeShiftDsl: + def apply[Domain, Value] = StackSafeShiftDsl[Domain, Domain, Value] extension [C, R, A](inline fa: C)(using inline notKeyword: util.NotGiven[ @@ -58,8 +80,10 @@ object Shift extends LowPriorityShift0 { transparent inline def unary_! : A = Dsl.shift[Shift[R, A], A](Shift[R, A](asContinuation(fa))): A - - private def shiftTailRec[R, Value](continuation: TailRec[R] !! Value, handler: Value => TailRec[R]) = { + private def shiftTailRec[R, Value]( + continuation: TailRec[R] !! Value, + handler: Value => TailRec[R] + ) = { continuation { a => val handler1 = handler TailCalls.tailcall(handler1(a)) @@ -67,14 +91,19 @@ object Shift extends LowPriorityShift0 { } @inline - implicit def tailRecShiftDsl[R, Value]: SameDomainStackSafeShiftDsl[TailRec[R], Value] = - new SameDomainStackSafeShiftDsl[TailRec[R], Value] { - def cpsApply(keyword: Shift[TailRec[R], Value], handler: Value => TailRec[R]): TailRec[R] = { + given tailRecShiftDsl[R, Value] + : SameDomainStackSafeShiftDsl[TailRec[R], Value] = + SameDomainStackSafeShiftDsl { + ( + keyword: Shift[TailRec[R], Value], + handler: Value => TailRec[R] + ) => shiftTailRec(keyword, handler) - } + } - private abstract class TrampolineContinuation[LeftDomain] extends (LeftDomain !! Throwable) { + private abstract class TrampolineContinuation[LeftDomain] + extends (LeftDomain !! Throwable) { protected def step(): LeftDomain !! Throwable @tailrec @@ -108,14 +137,14 @@ object Shift extends LowPriorityShift0 { } @inline - implicit def stackSafeThrowableShiftDsl[LeftDomain, Value] + given [LeftDomain, Value] : SameDomainStackSafeShiftDsl[LeftDomain !! Throwable, Value] = - new SameDomainStackSafeShiftDsl[LeftDomain !! Throwable, Value] { + SameDomainStackSafeShiftDsl[LeftDomain !! Throwable, Value] { - def cpsApply( + ( keyword: Shift[LeftDomain !! Throwable, Value], handler: Value => LeftDomain !! Throwable - ): !![LeftDomain, Throwable] = + ) => suspend(keyword, handler) } @@ -143,13 +172,20 @@ object Shift extends LowPriorityShift0 { } @inline - implicit def taskStackSafeShiftDsl[LeftDomain, RightDomain, Value] - : StackSafeShiftDsl[LeftDomain !! Throwable, LeftDomain !! Throwable !! RightDomain, Value] = - new StackSafeShiftDsl[LeftDomain !! Throwable, LeftDomain !! Throwable !! RightDomain, Value] { - def cpsApply( + given [LeftDomain, RightDomain, Value]: StackSafeShiftDsl[ + LeftDomain !! Throwable, + LeftDomain !! Throwable !! RightDomain, + Value + ] = + StackSafeShiftDsl[ + LeftDomain !! Throwable, + LeftDomain !! Throwable !! RightDomain, + Value + ] { + ( keyword: Shift[!![LeftDomain, Throwable], Value], handler: Value => !![!![LeftDomain, Throwable], RightDomain] - ): !![!![LeftDomain, Throwable], RightDomain] = + ) => taskFlatMap(keyword, handler) } diff --git a/keywords-Suspend/src/main/scala/com/thoughtworks/dsl/keywords/Suspend.scala b/keywords-Suspend/src/main/scala/com/thoughtworks/dsl/keywords/Suspend.scala index 6e96a86de..1348cee61 100644 --- a/keywords-Suspend/src/main/scala/com/thoughtworks/dsl/keywords/Suspend.scala +++ b/keywords-Suspend/src/main/scala/com/thoughtworks/dsl/keywords/Suspend.scala @@ -9,9 +9,11 @@ object Suspend { given[Upstream, UpstreamValue](using upstreamIsKeyword: => IsKeyword[Upstream, UpstreamValue]): IsKeyword[Suspend[Upstream], UpstreamValue] with {} - given[Keyword, Domain, Value](using Dsl.PolyCont[Keyword, Domain, Value]): Dsl.PolyCont[Suspend[Keyword], Domain, Value] with { - def cpsApply(keyword: Suspend[Keyword], handler: Value => Domain): Domain = { - keyword().cpsApply(handler) - } + given [Keyword, Domain, Value](using + Dsl.Searching[Keyword, Domain, Value] + ): Dsl.Composed[Suspend[Keyword], Domain, Value] = Dsl.Composed { + (keyword: Suspend[Keyword], handler: Value => Domain) => + keyword().cpsApply(handler) } -} \ No newline at end of file + +} diff --git a/keywords-TryCatch/src/main/scala/com/thoughtworks/dsl/keywords/TryCatch.scala b/keywords-TryCatch/src/main/scala/com/thoughtworks/dsl/keywords/TryCatch.scala index 08629bcb2..16e9c7568 100644 --- a/keywords-TryCatch/src/main/scala/com/thoughtworks/dsl/keywords/TryCatch.scala +++ b/keywords-TryCatch/src/main/scala/com/thoughtworks/dsl/keywords/TryCatch.scala @@ -1,7 +1,6 @@ package com.thoughtworks.dsl package keywords import Dsl.!! -import Dsl.cpsApply import Dsl.IsKeyword import scala.util.control.Exception.Catcher import scala.concurrent._ @@ -13,13 +12,13 @@ object TryCatch { given [Value, OuterDomain, BlockKeyword, BlockDomain, CaseKeyword]( using dslTryCatch: Dsl.TryCatch[Value, OuterDomain, BlockDomain], - blockDsl: Dsl.PolyCont[BlockKeyword, BlockDomain, Value], - caseDsl: Dsl.PolyCont[CaseKeyword, BlockDomain, Value], - ): Dsl.PolyCont[TryCatch[BlockKeyword, CaseKeyword], OuterDomain, Value] = { + blockDsl: Dsl.Searching[BlockKeyword, BlockDomain, Value], + caseDsl: Dsl.Searching[CaseKeyword, BlockDomain, Value], + ): Dsl.Composed[TryCatch[BlockKeyword, CaseKeyword], OuterDomain, Value] = Dsl.Composed { case (TryCatch(blockKeyword, cases), handler) => dslTryCatch.tryCatch( - blockDsl.cpsApply(blockKeyword, _), - cases.andThen { caseKeyword => caseDsl.cpsApply(caseKeyword, _) }, + blockDsl(blockKeyword, _), + cases.andThen { caseKeyword => caseDsl(caseKeyword, _) }, handler ) } diff --git a/keywords-TryCatchFinally/src/main/scala/com/thoughtworks/dsl/keywords/TryCatchFinally.scala b/keywords-TryCatchFinally/src/main/scala/com/thoughtworks/dsl/keywords/TryCatchFinally.scala index 1f968156e..6064ede8f 100644 --- a/keywords-TryCatchFinally/src/main/scala/com/thoughtworks/dsl/keywords/TryCatchFinally.scala +++ b/keywords-TryCatchFinally/src/main/scala/com/thoughtworks/dsl/keywords/TryCatchFinally.scala @@ -14,15 +14,15 @@ object TryCatchFinally { given [Value, OuterDomain, BlockKeyword, BlockDomain, CaseKeyword, FinalizerKeyword, FinalizerDomain]( using dslTryCatchFinally: Dsl.TryCatchFinally[Value, OuterDomain, BlockDomain, FinalizerDomain], - blockDsl: Dsl.PolyCont[BlockKeyword, BlockDomain, Value], - caseDsl: Dsl.PolyCont[CaseKeyword, BlockDomain, Value], - finalizerDsl: Dsl.PolyCont[FinalizerKeyword, FinalizerDomain, Unit] - ): Dsl.PolyCont[TryCatchFinally[BlockKeyword, CaseKeyword, FinalizerKeyword], OuterDomain, Value] = { + blockDsl: Dsl.Searching[BlockKeyword, BlockDomain, Value], + caseDsl: Dsl.Searching[CaseKeyword, BlockDomain, Value], + finalizerDsl: Dsl.Searching[FinalizerKeyword, FinalizerDomain, Unit] + ): Dsl.Composed[TryCatchFinally[BlockKeyword, CaseKeyword, FinalizerKeyword], OuterDomain, Value] = Dsl.Composed { case (TryCatchFinally(blockKeyword, cases, finalizerKeyword), handler) => dslTryCatchFinally.tryCatchFinally( - blockDsl.cpsApply(blockKeyword, _), - cases.andThen { caseKeyword => caseDsl.cpsApply(caseKeyword, _) }, - finalizerDsl.cpsApply(finalizerKeyword, _), + blockDsl(blockKeyword, _), + cases.andThen { caseKeyword => caseDsl(caseKeyword, _) }, + finalizerDsl(finalizerKeyword, _), handler ) } diff --git a/keywords-TryFinally/src/main/scala/com/thoughtworks/dsl/keywords/TryFinally.scala b/keywords-TryFinally/src/main/scala/com/thoughtworks/dsl/keywords/TryFinally.scala index 0ebac3cae..eaabdde35 100644 --- a/keywords-TryFinally/src/main/scala/com/thoughtworks/dsl/keywords/TryFinally.scala +++ b/keywords-TryFinally/src/main/scala/com/thoughtworks/dsl/keywords/TryFinally.scala @@ -1,7 +1,6 @@ package com.thoughtworks.dsl package keywords import Dsl.!! -import Dsl.cpsApply import Dsl.IsKeyword import scala.util.control.Exception.Catcher import scala.concurrent._ @@ -16,13 +15,13 @@ object TryFinally { given [Value, OuterDomain, BlockKeyword, BlockDomain, FinalizerKeyword, FinalizerDomain](using dslTryFinally: Dsl.TryFinally[Value, OuterDomain, BlockDomain, FinalizerDomain], - blockDsl: Dsl.PolyCont[BlockKeyword, BlockDomain, Value], - finalizerDsl: Dsl.PolyCont[FinalizerKeyword, FinalizerDomain, Unit] - ): Dsl.PolyCont[TryFinally[BlockKeyword, FinalizerKeyword], OuterDomain, Value] = { + blockDsl: Dsl.Searching[BlockKeyword, BlockDomain, Value], + finalizerDsl: Dsl.Searching[FinalizerKeyword, FinalizerDomain, Unit] + ): Dsl.Composed[TryFinally[BlockKeyword, FinalizerKeyword], OuterDomain, Value] = Dsl.Composed { case (TryFinally(blockKeyword, finalizerKeyword), handler) => dslTryFinally.tryFinally( - blockDsl.cpsApply(blockKeyword, _), - finalizerDsl.cpsApply(finalizerKeyword, _), + blockDsl(blockKeyword, _), + finalizerDsl(finalizerKeyword, _), handler ) } diff --git a/keywords-Typed/src/main/scala/com/thoughtworks/dsl/keywords/Typed.scala b/keywords-Typed/src/main/scala/com/thoughtworks/dsl/keywords/Typed.scala index b2bff0609..420a91967 100644 --- a/keywords-Typed/src/main/scala/com/thoughtworks/dsl/keywords/Typed.scala +++ b/keywords-Typed/src/main/scala/com/thoughtworks/dsl/keywords/Typed.scala @@ -8,9 +8,9 @@ object Typed { given [Keyword, Value] : Dsl.IsKeyword[Typed[Keyword, Value], Value] with {} given [Keyword, Domain, Value](using - dsl: Dsl.PolyCont[Keyword, Domain, Value] - ): Dsl.PolyCont[Typed[Keyword, Value], Domain, Value] = - dsl + dsl: Dsl.Searching[Keyword, Domain, Value] + ): Dsl.Composed[Typed[Keyword, Value], Domain, Value] = + Dsl.Composed(dsl) @inline def apply[Keyword, Value]: Keyword =:= Typed[Keyword, Value] = Dsl.Keyword.Opaque.Of.apply diff --git a/keywords-Using/src/main/scala/com/thoughtworks/dsl/keywords/Using.scala b/keywords-Using/src/main/scala/com/thoughtworks/dsl/keywords/Using.scala index 09307da8d..c282308f4 100644 --- a/keywords-Using/src/main/scala/com/thoughtworks/dsl/keywords/Using.scala +++ b/keywords-Using/src/main/scala/com/thoughtworks/dsl/keywords/Using.scala @@ -6,7 +6,6 @@ import com.thoughtworks.dsl.Dsl import com.thoughtworks.dsl.Dsl.!! import com.thoughtworks.dsl.Dsl.IsKeyword import com.thoughtworks.dsl.keywords.TryFinally -import com.thoughtworks.dsl.Dsl.cpsApply import scala.concurrent.{ExecutionContext, Future} import scala.language.implicitConversions @@ -86,10 +85,10 @@ object Using { ](using IsKeyword[Mapped, MappedValue], Dsl.TryFinally[MappedValue, OuterDomain, BlockDomain, FinalizerDomain], - Dsl.PolyCont[Mapped, BlockDomain, MappedValue] - ): Dsl.PolyCont[FlatMap[Using[R], Mapped], OuterDomain, MappedValue] = { + Dsl.Searching[Mapped, BlockDomain, MappedValue] + ): Dsl.Composed[FlatMap[Using[R], Mapped], OuterDomain, MappedValue] = Dsl.Composed { case (FlatMap(r, flatMapper: (Using[R] @unchecked => Mapped)), handler) => - reset { + reset[OuterDomain] { handler(try { !flatMapper(r) } finally { diff --git a/keywords-While/src/main/scala/com/thoughtworks/dsl/keywords/While.scala b/keywords-While/src/main/scala/com/thoughtworks/dsl/keywords/While.scala index ba47445cb..b153b0184 100644 --- a/keywords-While/src/main/scala/com/thoughtworks/dsl/keywords/While.scala +++ b/keywords-While/src/main/scala/com/thoughtworks/dsl/keywords/While.scala @@ -16,26 +16,21 @@ object While { BodyKeyword, Domain ](using - conditionDsl: Dsl.PolyCont[ConditionKeyword, Domain, Boolean], - bodyDsl: Dsl.PolyCont[BodyKeyword, Domain, Any] - ): Dsl.PolyCont[ + conditionDsl: Dsl.Searching[ConditionKeyword, Domain, Boolean], + bodyDsl: Dsl.Searching[BodyKeyword, Domain, Any] + ): Dsl.Composed[ While[ConditionKeyword, BodyKeyword], Domain, Unit - ] with { - def cpsApply( - keyword: While[ConditionKeyword, BodyKeyword], - handler: Unit => Domain - ): Domain = { + ] = Dsl.Composed { + (keyword: While[ConditionKeyword, BodyKeyword], handler: Unit => Domain) => keyword.condition.cpsApply { case true => keyword.body.cpsApply { _ => - cpsApply(keyword, handler) + keyword.cpsApply(handler) } case false => handler(()) } - } - } } diff --git a/keywords-Yield/src/main/scala/com/thoughtworks/dsl/keywords/Yield.scala b/keywords-Yield/src/main/scala/com/thoughtworks/dsl/keywords/Yield.scala index 5a308c73d..16043ed39 100644 --- a/keywords-Yield/src/main/scala/com/thoughtworks/dsl/keywords/Yield.scala +++ b/keywords-Yield/src/main/scala/com/thoughtworks/dsl/keywords/Yield.scala @@ -13,33 +13,39 @@ import scala.concurrent.Future import scala.language.implicitConversions import scala.language.higherKinds -/** @author 杨博 (Yang Bo) - * @example This `Yield` keyword must be put inside a function that returns `Seq[Element]` or `Seq[Element] !! ...`, - * or it will not compile. +/** @author + * 杨博 (Yang Bo) + * @example + * This `Yield` keyword must be put inside a function that returns + * `Seq[Element]` or `Seq[Element] !! ...`, or it will not compile. * - * {{{ - * import com.thoughtworks.dsl.reset, reset._ - * "def f(): Int = !Yield(1)" shouldNot compile - * }}} + * {{{ + * import com.thoughtworks.dsl.reset, reset._ + * "def f(): Int = !Yield(1)" shouldNot compile + * }}} * - * @example [[Yield]] keywords can be used together with other keywords. - * {{{ - * import com.thoughtworks.dsl.reset, reset._ - * def gccFlagBuilder(sourceFile: String, includes: String*) = reset[Stream[String]] { - * !Yield("gcc") - * !Yield("-c") - * !Yield(sourceFile) - * val include = !Each(includes) - * !Yield("-I") - * !Yield(include) - * Stream.empty - * } + * @example + * [[Yield]] keywords can be used together with other keywords. + * {{{ + * import com.thoughtworks.dsl.reset, reset._ + * def gccFlagBuilder(sourceFile: String, includes: String*) = reset[Stream[String]] { + * !Yield("gcc") + * !Yield("-c") + * !Yield(sourceFile) + * val include = !Each(includes) + * !Yield("-I") + * !Yield(include) + * Stream.empty + * } * - * gccFlagBuilder("main.c", "lib1/include", "lib2/include") should be(Stream("gcc", "-c", "main.c", "-I", "lib1/include", "-I", "lib2/include")) - * }}} - * @see [[comprehension]] if you want to use traditional `for` comprehension instead of !-notation. + * gccFlagBuilder("main.c", "lib1/include", "lib2/include") should be(Stream("gcc", "-c", "main.c", "-I", "lib1/include", "-I", "lib2/include")) + * }}} + * @see + * [[comprehension]] if you want to use traditional `for` comprehension + * instead of !-notation. */ -opaque type Yield[+Element] <: Dsl.Keyword.Opaque = Dsl.Keyword.Opaque.Of[Element] +opaque type Yield[+Element] <: Dsl.Keyword.Opaque = + Dsl.Keyword.Opaque.Of[Element] private[keywords] trait LowPriorityYield3 { @@ -47,43 +53,38 @@ private[keywords] trait LowPriorityYield3 { From(elements) } - implicit def iteratorYieldFromDsl[A, FromCollection <: TraversableOnce[A]] - : Dsl[From[FromCollection], Iterator[A], Unit] = - new Dsl[From[FromCollection], Iterator[A], Unit] { - def cpsApply(keyword: From[FromCollection], generateTail: Unit => Iterator[A]): Iterator[A] = { + given [A, FromCollection <: TraversableOnce[A]] + : Dsl.Atomic[From[FromCollection], Iterator[A], Unit] = + Dsl.Atomic[From[FromCollection], Iterator[A], Unit] { + (keyword: From[FromCollection], generateTail: Unit => Iterator[A]) => From.apply.flip(keyword).toIterator ++ generateTail(()) - } } - implicit def iteratorYieldDsl[A, B >: A]: Dsl[Yield[A], Iterator[B], Unit] = - new Dsl[Yield[A], Iterator[B], Unit] { - def cpsApply(keyword: Yield[A], generateTail: Unit => Iterator[B]): Iterator[B] = { + given [A, B >: A]: Dsl.Atomic[Yield[A], Iterator[B], Unit] = + Dsl.Atomic[Yield[A], Iterator[B], Unit] { + (keyword: Yield[A], generateTail: Unit => Iterator[B]) => Iterator.single(Yield.apply.flip(keyword)) ++ generateTail(()) - } } - } - private[keywords] trait LowPriorityYield1 extends LowPriorityYield3 { - implicit def seqYieldFromDsl[A, FromCollection <: View.SomeIterableOps[A], Collection[X] <: SeqOps[ + given [A, FromCollection <: View.SomeIterableOps[A], Collection[X] <: SeqOps[ X, Collection, Collection[X] - ]]: Dsl[From[FromCollection], Collection[A], Unit] = - new Dsl[From[FromCollection], Collection[A], Unit] { - def cpsApply(keyword: From[FromCollection], generateTail: Unit => Collection[A]): Collection[A] = { + ]]: Dsl.Atomic[From[FromCollection], Collection[A], Unit] = + Dsl.Atomic[From[FromCollection], Collection[A], Unit] { + (keyword: From[FromCollection], generateTail: Unit => Collection[A]) => From.apply.flip(keyword).toIterable ++: generateTail(()) - } } - implicit def seqYieldDsl[A, B >: A, Collection[+X] <: SeqOps[X, Collection, Collection[X]]] - : Dsl[Yield[A], Collection[B], Unit] = - new Dsl[Yield[A], Collection[B], Unit] { - def cpsApply(keyword: Yield[A], generateTail: Unit => Collection[B]): Collection[B] = { + given [A, B >: A, Collection[+X] <: SeqOps[X, Collection, Collection[X]]] + : Dsl.Atomic[Yield[A], Collection[B], Unit] = + Dsl.Atomic[Yield[A], Collection[B], Unit] { + (keyword: Yield[A], generateTail: Unit => Collection[B]) => Yield.apply.flip(keyword) +: generateTail(()) - } } + } private[keywords] trait LowPriorityYield0 extends LowPriorityYield1 @@ -95,11 +96,40 @@ object Yield extends LowPriorityYield0 { From(element0 +: element1 +: elements) } - opaque type From[FromCollection <: TraversableOnce[_]] <: Dsl.Keyword.Opaque = Dsl.Keyword.Opaque.Of[FromCollection] - object From { - given [FromCollection <: TraversableOnce[_]]: IsKeyword[From[FromCollection], Unit] with {} + opaque type From[FromCollection <: TraversableOnce[_]] <: Dsl.Keyword.Opaque = + Dsl.Keyword.Opaque.Of[FromCollection] + + private[Yield] trait LowPriorityFrom0 { this: From.type => + + given [A, FromSomeIterableOps <: View.SomeIterableOps[A]] + : Dsl.Atomic[From[FromSomeIterableOps], SeqView[A], Unit] = + Dsl.Atomic[From[FromSomeIterableOps], SeqView[A], Unit] { + ( + keyword: From[FromSomeIterableOps], + generateTail: Unit => SeqView[A] + ) => + new SeqView.Concat(collection.Seq.from(keyword), generateTail(())) + } + + given [A, FromSomeIterableOps <: View.SomeIterableOps[A]] + : Dsl.Atomic[From[FromSomeIterableOps], IndexedSeqView[A], Unit] = + Dsl.Atomic[From[FromSomeIterableOps], IndexedSeqView[A], Unit] { + ( + keyword: From[FromSomeIterableOps], + generateTail: Unit => IndexedSeqView[A] + ) => + new IndexedSeqView.Concat( + collection.IndexedSeq.from(keyword), + generateTail(()) + ) + } + } + object From extends LowPriorityFrom0 { + given [FromCollection <: TraversableOnce[_]] + : IsKeyword[From[FromCollection], Unit] with {} - def apply[FromCollection <: TraversableOnce[_]]: FromCollection =:= From[FromCollection] = Dsl.Keyword.Opaque.Of.apply + def apply[FromCollection <: TraversableOnce[_]] + : FromCollection =:= From[FromCollection] = Dsl.Keyword.Opaque.Of.apply extension [A, E](inline a: A)(using inline notKeyword: util.NotGiven[ @@ -109,94 +139,75 @@ object Yield extends LowPriorityYield0 { ) transparent inline def unary_! : Unit = !From[Iterable[E]](isIterable(a)) - } - - - implicit def viewYieldFromDsl[A, FromCollection <: View.SomeIterableOps[A]] - : Dsl[From[FromCollection], View[A], Unit] = - new Dsl[From[FromCollection], View[A], Unit] { - def cpsApply(keyword: From[FromCollection], generateTail: Unit => View[A]): View[A] = { - new View.Concat(keyword, generateTail(())) + given [A, FromCollection <: View.SomeIterableOps[A]] + : Dsl.Atomic[From[FromCollection], View[A], Unit] = + Dsl.Atomic[From[FromCollection], View[A], Unit] { + (keyword: From[FromCollection], generateTail: Unit => View[A]) => + new View.Concat(keyword, generateTail(())) } - } - implicit def indexedSeqViewYieldFromIterableDsl[A, FromCollection <: View.SomeIterableOps[A]] - : Dsl[From[FromCollection], IndexedSeqView[A], Unit] = - new Dsl[From[FromCollection], IndexedSeqView[A], Unit] { - def cpsApply(keyword: From[FromCollection], generateTail: Unit => IndexedSeqView[A]): IndexedSeqView[A] = { - new IndexedSeqView.Concat(collection.IndexedSeq.from(keyword), generateTail(())) + given indexedSeqViewYieldFromDsl[A, FromIndexedSeqOps <: IndexedSeqOps[ + A, + CC, + C + ], CC[_], C]: Dsl.Atomic[From[FromIndexedSeqOps], IndexedSeqView[A], Unit] = + Dsl.Atomic[From[FromIndexedSeqOps], IndexedSeqView[A], Unit] { + ( + keyword: From[FromIndexedSeqOps], + generateTail: Unit => IndexedSeqView[A] + ) => + new IndexedSeqView.Concat(keyword, generateTail(())) } - } - implicit def indexedSeqViewYieldFromDsl[A, FromCollection <: IndexedSeqOps[A, CC, C], CC[_], C] - : Dsl[From[FromCollection], IndexedSeqView[A], Unit] = - new Dsl[From[FromCollection], IndexedSeqView[A], Unit] { - def cpsApply(keyword: From[FromCollection], generateTail: Unit => IndexedSeqView[A]): IndexedSeqView[A] = { - new IndexedSeqView.Concat(keyword, generateTail(())) + given seqViewYieldFromDsl[A, FromCollection <: SeqOps[A, CC, C], CC[_], C] + : Dsl.Atomic[From[FromCollection], SeqView[A], Unit] = + Dsl.Atomic[From[FromCollection], SeqView[A], Unit] { + (keyword: From[FromCollection], generateTail: Unit => SeqView[A]) => + new SeqView.Concat(keyword, generateTail(())) } - } - implicit def seqViewYieldFromIterableDsl[A, FromCollection <: View.SomeIterableOps[A]] - : Dsl[From[FromCollection], SeqView[A], Unit] = - new Dsl[From[FromCollection], SeqView[A], Unit] { - def cpsApply(keyword: From[FromCollection], generateTail: Unit => SeqView[A]): SeqView[A] = { - new SeqView.Concat(collection.Seq.from(keyword), generateTail(())) + given [A, FromIterable <: Iterable[A]] + : Dsl.Atomic[From[FromIterable], LazyList[A], Unit] = + Dsl.Atomic[From[FromIterable], LazyList[A], Unit] { + (keyword: From[FromIterable], generateTail: Unit => LazyList[A]) => + keyword.to(LazyList) #::: generateTail(()) } - } - implicit def seqViewYieldFromDsl[A, FromCollection <: SeqOps[A, CC, C], CC[_], C] - : Dsl[From[FromCollection], SeqView[A], Unit] = - new Dsl[From[FromCollection], SeqView[A], Unit] { - def cpsApply(keyword: From[FromCollection], generateTail: Unit => SeqView[A]): SeqView[A] = { - new SeqView.Concat(keyword, generateTail(())) + given [A, FromIterable <: Iterable[A]] + : Dsl.Atomic[From[FromIterable], Stream[A], Unit] = + Dsl.Atomic[From[FromIterable], Stream[A], Unit] { + (keyword: From[FromIterable], generateTail: Unit => Stream[A]) => + keyword.toStream #::: generateTail(()) } - } - implicit def seqViewYieldDsl[A, B >: A]: Dsl[Yield[A], SeqView[B], Unit] = - new Dsl[Yield[A], SeqView[B], Unit] { - def cpsApply(keyword: Yield[A], generateTail: Unit => SeqView[B]): SeqView[B] = { + } + + given [A, B >: A]: Dsl.Atomic[Yield[A], SeqView[B], Unit] = + Dsl.Atomic[Yield[A], SeqView[B], Unit] { + (keyword: Yield[A], generateTail: Unit => SeqView[B]) => generateTail(()).prepended(keyword) - } } - implicit def indexedSeqViewYieldDsl[A, B >: A]: Dsl[Yield[A], IndexedSeqView[B], Unit] = - new Dsl[Yield[A], IndexedSeqView[B], Unit] { - def cpsApply(keyword: Yield[A], generateTail: Unit => IndexedSeqView[B]): IndexedSeqView[B] = { + given [A, B >: A]: Dsl.Atomic[Yield[A], IndexedSeqView[B], Unit] = + Dsl.Atomic[Yield[A], IndexedSeqView[B], Unit] { + (keyword: Yield[A], generateTail: Unit => IndexedSeqView[B]) => generateTail(()).prepended(keyword) - } } - implicit def viewYieldDsl[A, B >: A]: Dsl[Yield[A], View[B], Unit] = - new Dsl[Yield[A], View[B], Unit] { - def cpsApply(keyword: Yield[A], generateTail: Unit => View[B]): View[B] = { + given [A, B >: A]: Dsl.Atomic[Yield[A], View[B], Unit] = + Dsl.Atomic[Yield[A], View[B], Unit] { + (keyword: Yield[A], generateTail: Unit => View[B]) => new View.Concat[B](new View.Single(keyword), generateTail(())) - } - } - - implicit def lazyListYieldFromDsl[A, FromCollection <: Iterable[A]]: Dsl[From[FromCollection], LazyList[A], Unit] = - new Dsl[From[FromCollection], LazyList[A], Unit] { - def cpsApply(keyword: From[FromCollection], generateTail: Unit => LazyList[A]): LazyList[A] = { - keyword.to(LazyList) #::: generateTail(()) - } } - implicit def lazyListYieldDsl[Element, That >: Element]: Dsl[Yield[Element], LazyList[That], Unit] = - new Dsl[Yield[Element], LazyList[That], Unit] { - def cpsApply(keyword: Yield[Element], generateTail: Unit => LazyList[That]): LazyList[That] = { + given [Element, That >: Element] + : Dsl.Atomic[Yield[Element], LazyList[That], Unit] = + Dsl.Atomic[Yield[Element], LazyList[That], Unit] { + (keyword: Yield[Element], generateTail: Unit => LazyList[That]) => keyword #:: generateTail(()) - } - } - - - implicit def streamYieldFromDsl[A, FromCollection <: Iterable[A]]: Dsl[From[FromCollection], Stream[A], Unit] = - new Dsl[From[FromCollection], Stream[A], Unit] { - def cpsApply(keyword: From[FromCollection], generateTail: Unit => Stream[A]): Stream[A] = { - keyword.toStream #::: generateTail(()) - } } - extension [A](inline a: A)(using inline notKeyword: util.NotGiven[ A <:< Dsl.Keyword @@ -205,11 +216,10 @@ object Yield extends LowPriorityYield0 { transparent inline def unary_! : Unit = !Yield[A](a) - implicit def streamYieldDsl[Element, That >: Element]: Dsl[Yield[Element], Stream[That], Unit] = - new Dsl[Yield[Element], Stream[That], Unit] { - def cpsApply(keyword: Yield[Element], generateTail: Unit => Stream[That]): Stream[That] = { + implicit def streamYieldDsl[Element, That >: Element] + : Dsl.Atomic[Yield[Element], Stream[That], Unit] = + Dsl.Atomic[Yield[Element], Stream[That], Unit] { + (keyword: Yield[Element], generateTail: Unit => Stream[That]) => keyword #:: generateTail(()) - } } - } diff --git a/reset/src/main/scala/com/thoughtworks/dsl/reset.scala b/reset/src/main/scala/com/thoughtworks/dsl/reset.scala index d8abb3513..c4efdfbe9 100644 --- a/reset/src/main/scala/com/thoughtworks/dsl/reset.scala +++ b/reset/src/main/scala/com/thoughtworks/dsl/reset.scala @@ -137,7 +137,7 @@ object reset { helper } - val List(shiftSymbol) = Symbol.classSymbol("com.thoughtworks.dsl.Dsl$").declaredMethod("shift") + val List(shiftSymbol) = Symbol.requiredModule("com.thoughtworks.dsl.Dsl").declaredMethod("shift") sealed trait KeywordTree { def keywordTerm: Term