From f120b22c6afbe7c52ff1ea8398b325b25f6160e6 Mon Sep 17 00:00:00 2001 From: "Yang, Bo" Date: Thu, 23 Dec 2021 21:41:55 -0800 Subject: [PATCH 1/7] Add a test for O(2^n) algorithm --- .../com/thoughtworks/dsl/domains/taskSpec.scala | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/domains-Task/.jvm/src/test/scala/com/thoughtworks/dsl/domains/taskSpec.scala b/domains-Task/.jvm/src/test/scala/com/thoughtworks/dsl/domains/taskSpec.scala index 02c01c813..16af242d5 100644 --- a/domains-Task/.jvm/src/test/scala/com/thoughtworks/dsl/domains/taskSpec.scala +++ b/domains-Task/.jvm/src/test/scala/com/thoughtworks/dsl/domains/taskSpec.scala @@ -21,6 +21,21 @@ import org.scalatest.matchers.should.Matchers */ final class taskSpec extends AsyncFreeSpec with Matchers { + "O(2^n) algorithm should not stack overflow" in { + def fibonacci(n: Int): Task[Int] = Task { + n match { + case 0 => + 0 + case 1 => + 1 + case _ => + !Shift(fibonacci(n - 1)) + !Shift(fibonacci(n - 2)) + } + } + Task.toFuture(fibonacci(25)).map { + _ should be(75025) + } + } "tailRecursion" in Task.toFuture(Task { def loop(i: Int = 0, accumulator: Int = 0): Task[Int] = Task { if (i < 10000) { From a3928f77d5c05bf1eafd27d9c59150f2f61b387b Mon Sep 17 00:00:00 2001 From: "Yang, Bo" Date: Thu, 23 Dec 2021 21:49:58 -0800 Subject: [PATCH 2/7] Add type assertion for taskSpec reification --- .../thoughtworks/dsl/domains/taskSpec.scala | 69 +++++++++++++++---- 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/domains-Task/.jvm/src/test/scala/com/thoughtworks/dsl/domains/taskSpec.scala b/domains-Task/.jvm/src/test/scala/com/thoughtworks/dsl/domains/taskSpec.scala index 16af242d5..d8bf28c32 100644 --- a/domains-Task/.jvm/src/test/scala/com/thoughtworks/dsl/domains/taskSpec.scala +++ b/domains-Task/.jvm/src/test/scala/com/thoughtworks/dsl/domains/taskSpec.scala @@ -6,9 +6,8 @@ import com.thoughtworks.dsl.Dsl.{!!} import org.scalatest.Assertion import scala.language.implicitConversions -import com.thoughtworks.dsl.keywords.{Using, Each} +import com.thoughtworks.dsl.keywords.*, Match.+: import com.thoughtworks.dsl.domains._ -import com.thoughtworks.dsl.keywords.Shift import scala.collection.mutable.ArrayBuffer import scala.util.control.TailCalls @@ -22,14 +21,42 @@ import org.scalatest.matchers.should.Matchers final class taskSpec extends AsyncFreeSpec with Matchers { "O(2^n) algorithm should not stack overflow" in { - def fibonacci(n: Int): Task[Int] = Task { - n match { - case 0 => - 0 - case 1 => - 1 - case _ => - !Shift(fibonacci(n - 1)) + !Shift(fibonacci(n - 2)) + def fibonacci(n: Int): Task[Int] = { + val reified = reify { + n match { + case 0 => + 0 + case 1 => + 1 + case _ => + !Shift(fibonacci(n - 1)) + !Shift(fibonacci(n - 2)) + } + } + summon[ + reified.type <:< + Typed[FlatMap[ + Pure[n.type], + Match.WithIndex[(0), Pure[(0)]] + +: + Match.WithIndex[(1), Pure[(1)]] + +: + Match.WithIndex[(2), FlatMap[ + Shift[Task.TaskDomain, Int], + FlatMap[Shift[Task.TaskDomain, Int], Pure[Int]] + ]] + +: Nothing + ], Int] + ] + Task { + + n match { + case 0 => + 0 + case 1 => + 1 + case _ => + !Shift(fibonacci(n - 1)) + !Shift(fibonacci(n - 2)) + } } } Task.toFuture(fibonacci(25)).map { @@ -37,11 +64,27 @@ final class taskSpec extends AsyncFreeSpec with Matchers { } } "tailRecursion" in Task.toFuture(Task { - def loop(i: Int = 0, accumulator: Int = 0): Task[Int] = Task { - if (i < 10000) { + def loop(i: Int = 0, accumulator: Int = 0): Task[Int] = { + val reified = reify(if (i < 10000) { !Shift(loop(i + 1, accumulator + i)) } else { accumulator + }) + summon[reified.type <:< Typed[ + If[Pure[ + Boolean + ], Shift[ + Task.TaskDomain, + Int + ], Pure[Int]], + Int + ]] + Task { + if (i < 10000) { + !Shift(loop(i + 1, accumulator + i)) + } else { + accumulator + } } } @@ -57,7 +100,7 @@ final class taskSpec extends AsyncFreeSpec with Matchers { val task1: Task[Int] = Task.now(1) - val ts = *[Task]/* .join */ apply Seq { + val ts = *[Task] /* .join */ apply Seq { !Each(0 until 10) + !Shift(task1) } From fab207e02b7cbbcf4cb5ee1536e4e8843279c765 Mon Sep 17 00:00:00 2001 From: "Yang, Bo" Date: Thu, 23 Dec 2021 23:42:23 -0800 Subject: [PATCH 3/7] Deprecate Dsl.TryCatch / Dsl.TryFinally / Dsl.TryCatchFinally --- .../main/scala/com/thoughtworks/dsl/Dsl.scala | 12 ++++++ .../thoughtworks/dsl/keywords/TryCatch.scala | 33 ++++++++------- .../dsl/keywords/TryCatchFinally.scala | 40 ++++++++++--------- .../dsl/keywords/TryFinally.scala | 34 +++++++++------- 4 files changed, 72 insertions(+), 47 deletions(-) diff --git a/Dsl/src/main/scala/com/thoughtworks/dsl/Dsl.scala b/Dsl/src/main/scala/com/thoughtworks/dsl/Dsl.scala index 2134c3535..6e77f8fe8 100644 --- a/Dsl/src/main/scala/com/thoughtworks/dsl/Dsl.scala +++ b/Dsl/src/main/scala/com/thoughtworks/dsl/Dsl.scala @@ -418,6 +418,10 @@ object Dsl extends LowPriorityDsl0 { @implicitNotFound( "The `try` ... `catch` ... `finally` expression cannot contain !-notation inside a function that returns ${OuterDomain}." ) + @deprecated( + "Dsl.TryCatch / Dsl.TryFinally / Dsl.TryCatchFinally will be removed", + "2.0.0" + ) trait TryCatchFinally[Value, OuterDomain, BlockDomain, FinalizerDomain] { def tryCatchFinally( block: BlockDomain !! Value, @@ -462,6 +466,10 @@ object Dsl extends LowPriorityDsl0 { @implicitNotFound( "The `try` ... `catch` expression cannot contain !-notation inside a function that returns ${OuterDomain}." ) + @deprecated( + "Dsl.TryCatch / Dsl.TryFinally / Dsl.TryCatchFinally will be removed", + "2.0.0" + ) trait TryCatch[Value, OuterDomain, BlockDomain] { def tryCatch( block: BlockDomain !! Value, @@ -582,6 +590,10 @@ object Dsl extends LowPriorityDsl0 { @implicitNotFound( "The `try` ... `finally` expression cannot contain !-notation inside a function that returns ${OuterDomain}." ) + @deprecated( + "Dsl.TryCatch / Dsl.TryFinally / Dsl.TryCatchFinally will be removed", + "2.0.0" + ) trait TryFinally[Value, OuterDomain, BlockDomain, FinalizerDomain] { def tryFinally( block: BlockDomain !! Value, 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 1ec719f8f..99471f486 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 @@ -10,20 +10,25 @@ case class TryCatch[+BlockKeyword, +CaseKeyword]( block: () => BlockKeyword, cases: Catcher[CaseKeyword] ) extends Dsl.Keyword.Trait -object TryCatch { +object TryCatch extends TryCatch.LegacyInstances { - given [Value, OuterDomain, BlockKeyword, BlockDomain, CaseKeyword](using - dslTryCatch: Dsl.TryCatch[Value, OuterDomain, BlockDomain], - 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( - // TODO: Use Suspend to catch the exception - blockDsl(blockKeyword(), _), - cases.andThen { caseKeyword => caseDsl(caseKeyword, _) }, - handler - ) - } + trait LegacyInstances: + @deprecated( + "Dsl.TryCatch / Dsl.TryFinally / Dsl.TryCatchFinally will be removed", + "2.0.0" + ) + given [Value, OuterDomain, BlockKeyword, BlockDomain, CaseKeyword](using + dslTryCatch: Dsl.TryCatch[Value, OuterDomain, BlockDomain], + 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( + // TODO: Use Suspend to catch the exception + 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 c0ecabb97..3a727386a 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 @@ -9,23 +9,27 @@ case class TryCatchFinally[+BlockKeyword, +CaseKeyword, +FinalizerKeyword]( cases: Catcher[CaseKeyword], finalizer: () => FinalizerKeyword ) extends Dsl.Keyword.Trait -object TryCatchFinally { - - given [Value, OuterDomain, BlockKeyword, BlockDomain, CaseKeyword, FinalizerKeyword, FinalizerDomain]( - using - dslTryCatchFinally: Dsl.TryCatchFinally[Value, OuterDomain, BlockDomain, FinalizerDomain], - 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( - // TODO: Use Suspend to catch the exception - blockDsl(blockKeyword(), _), - cases.andThen { caseKeyword => caseDsl(caseKeyword, _) }, - finalizerDsl(finalizerKeyword(), _), - handler - ) - } +object TryCatchFinally extends TryCatchFinally.LegacyInstances { + trait LegacyInstances: + @deprecated( + "Dsl.TryCatch / Dsl.TryFinally / Dsl.TryCatchFinally will be removed", + "2.0.0" + ) + given [Value, OuterDomain, BlockKeyword, BlockDomain, CaseKeyword, FinalizerKeyword, FinalizerDomain]( + using + dslTryCatchFinally: Dsl.TryCatchFinally[Value, OuterDomain, BlockDomain, FinalizerDomain], + 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( + // TODO: Use Suspend to catch the exception + 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 970f74cf3..8df8f5872 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 @@ -11,20 +11,24 @@ case class TryFinally[+TryKeyword, +FinalizerKeyword]( finalizer: () => FinalizerKeyword ) extends Dsl.Keyword.Trait -object TryFinally { - - given [Value, OuterDomain, BlockKeyword, BlockDomain, FinalizerKeyword, FinalizerDomain](using - dslTryFinally: Dsl.TryFinally[Value, OuterDomain, BlockDomain, FinalizerDomain], - 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( - // TODO: Use Suspend to catch the exception - blockDsl(blockKeyword(), _), - finalizerDsl(finalizerKeyword(), _), - handler - ) - } +object TryFinally extends TryFinally.LegacyInstances { + trait LegacyInstances: + @deprecated( + "Dsl.TryCatch / Dsl.TryFinally / Dsl.TryCatchFinally will be removed", + "2.0.0" + ) + given [Value, OuterDomain, BlockKeyword, BlockDomain, FinalizerKeyword, FinalizerDomain](using + dslTryFinally: Dsl.TryFinally[Value, OuterDomain, BlockDomain, FinalizerDomain], + 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( + // TODO: Use Suspend to catch the exception + blockDsl(blockKeyword(), _), + finalizerDsl(finalizerKeyword(), _), + handler + ) + } } From aa0cb751b439db020bd3d5a516ade05cca6b0c66 Mon Sep 17 00:00:00 2001 From: "Yang, Bo" Date: Thu, 23 Dec 2021 23:42:28 -0800 Subject: [PATCH 4/7] Add Dsl.Composed[TryCatchFinally[...]] instance which does not depend on Dsl.TryCatchFinally --- .../dsl/keywords/TryCatchFinally.scala | 53 +++++++++++++++++-- 1 file changed, 49 insertions(+), 4 deletions(-) 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 3a727386a..0b0ed4a88 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 @@ -10,18 +10,63 @@ case class TryCatchFinally[+BlockKeyword, +CaseKeyword, +FinalizerKeyword]( finalizer: () => FinalizerKeyword ) extends Dsl.Keyword.Trait object TryCatchFinally extends TryCatchFinally.LegacyInstances { + given [ + Value, + OuterDomain, + BlockKeyword, + BlockDomain, + CaseKeyword, + FinalizerKeyword, + FinalizerDomain + ](using + Dsl.Searching[TryFinally[ + TryCatch[BlockKeyword, CaseKeyword], + FinalizerKeyword + ], OuterDomain, Value] + ): Dsl.Composed[TryCatchFinally[ + BlockKeyword, + CaseKeyword, + FinalizerKeyword + ], OuterDomain, Value] = Dsl.Composed { + case ( + TryCatchFinally( + block, + catcher, + finalizer + ), + handler + ) => + TryFinally(TryCatch(block, catcher), finalizer).cpsApply(handler) + } + trait LegacyInstances: @deprecated( "Dsl.TryCatch / Dsl.TryFinally / Dsl.TryCatchFinally will be removed", "2.0.0" ) - given [Value, OuterDomain, BlockKeyword, BlockDomain, CaseKeyword, FinalizerKeyword, FinalizerDomain]( - using - dslTryCatchFinally: Dsl.TryCatchFinally[Value, OuterDomain, BlockDomain, FinalizerDomain], + given [ + Value, + OuterDomain, + BlockKeyword, + BlockDomain, + CaseKeyword, + FinalizerKeyword, + FinalizerDomain + ](using + dslTryCatchFinally: Dsl.TryCatchFinally[ + Value, + OuterDomain, + BlockDomain, + FinalizerDomain + ], 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 { + ): Dsl.Composed[TryCatchFinally[ + BlockKeyword, + CaseKeyword, + FinalizerKeyword + ], OuterDomain, Value] = Dsl.Composed { case (TryCatchFinally(blockKeyword, cases, finalizerKeyword), handler) => dslTryCatchFinally.tryCatchFinally( // TODO: Use Suspend to catch the exception From 6be9bcb1f88bab5a5c858091f70fd6515acf414e Mon Sep 17 00:00:00 2001 From: "Yang, Bo" Date: Thu, 23 Dec 2021 23:43:14 -0800 Subject: [PATCH 5/7] Remove Dsl.TryCatchFinally --- .../main/scala/com/thoughtworks/dsl/Dsl.scala | 55 ------------------- .../dsl/keywords/TryCatchFinally.scala | 43 +-------------- 2 files changed, 2 insertions(+), 96 deletions(-) diff --git a/Dsl/src/main/scala/com/thoughtworks/dsl/Dsl.scala b/Dsl/src/main/scala/com/thoughtworks/dsl/Dsl.scala index 6e77f8fe8..05d88db11 100644 --- a/Dsl/src/main/scala/com/thoughtworks/dsl/Dsl.scala +++ b/Dsl/src/main/scala/com/thoughtworks/dsl/Dsl.scala @@ -408,61 +408,6 @@ object Dsl extends LowPriorityDsl0 { } } - /** 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. - */ - @implicitNotFound( - "The `try` ... `catch` ... `finally` expression cannot contain !-notation inside a function that returns ${OuterDomain}." - ) - @deprecated( - "Dsl.TryCatch / Dsl.TryFinally / Dsl.TryCatchFinally will be removed", - "2.0.0" - ) - trait TryCatchFinally[Value, OuterDomain, BlockDomain, FinalizerDomain] { - def tryCatchFinally( - block: BlockDomain !! Value, - catcher: Catcher[BlockDomain !! Value], - finalizer: FinalizerDomain !! Unit, - outerSuccessHandler: Value => OuterDomain - ): OuterDomain - } - - object TryCatchFinally { - - implicit def fromTryCatchTryFinally[ - Value, - OuterDomain, - BlockDomain, - FinalizerDomain - ](implicit - tryFinally: TryFinally[ - Value, - OuterDomain, - BlockDomain, - FinalizerDomain - ], - tryCatch: TryCatch[Value, BlockDomain, BlockDomain] - ): TryCatchFinally[Value, OuterDomain, BlockDomain, FinalizerDomain] = { - ( - block: BlockDomain !! Value, - catcher: Catcher[BlockDomain !! Value], - finalizer: FinalizerDomain !! Unit, - outerSuccessHandler: Value => OuterDomain - ) => - tryFinally.tryFinally( - { - tryCatch.tryCatch(block, catcher, _) - }, - finalizer, - outerSuccessHandler - ) - } - } - @implicitNotFound( "The `try` ... `catch` expression cannot contain !-notation inside a function that returns ${OuterDomain}." ) 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 0b0ed4a88..b6d9f4da9 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 @@ -9,7 +9,7 @@ case class TryCatchFinally[+BlockKeyword, +CaseKeyword, +FinalizerKeyword]( cases: Catcher[CaseKeyword], finalizer: () => FinalizerKeyword ) extends Dsl.Keyword.Trait -object TryCatchFinally extends TryCatchFinally.LegacyInstances { +object TryCatchFinally { given [ Value, OuterDomain, @@ -36,45 +36,6 @@ object TryCatchFinally extends TryCatchFinally.LegacyInstances { ), handler ) => - TryFinally(TryCatch(block, catcher), finalizer).cpsApply(handler) + TryFinally(() => TryCatch(block, catcher), finalizer).cpsApply(handler) } - - trait LegacyInstances: - @deprecated( - "Dsl.TryCatch / Dsl.TryFinally / Dsl.TryCatchFinally will be removed", - "2.0.0" - ) - given [ - Value, - OuterDomain, - BlockKeyword, - BlockDomain, - CaseKeyword, - FinalizerKeyword, - FinalizerDomain - ](using - dslTryCatchFinally: Dsl.TryCatchFinally[ - Value, - OuterDomain, - BlockDomain, - FinalizerDomain - ], - 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( - // TODO: Use Suspend to catch the exception - blockDsl(blockKeyword(), _), - cases.andThen { caseKeyword => caseDsl(caseKeyword, _) }, - finalizerDsl(finalizerKeyword(), _), - handler - ) - } - } From ff0bb04b5669bd14dbbb61964d5b7c0b88ce993c Mon Sep 17 00:00:00 2001 From: "Yang, Bo" Date: Thu, 23 Dec 2021 23:44:56 -0800 Subject: [PATCH 6/7] Rename Dsl.TryCatch to keywords.TryCatch.DslComposer --- .../main/scala/com/thoughtworks/dsl/Dsl.scala | 124 ---------- build.sbt | 2 + .../com/thoughtworks/dsl/domains/scalaz.scala | 62 ++--- .../thoughtworks/dsl/keywords/TryCatch.scala | 216 ++++++++++++++++-- 4 files changed, 230 insertions(+), 174 deletions(-) diff --git a/Dsl/src/main/scala/com/thoughtworks/dsl/Dsl.scala b/Dsl/src/main/scala/com/thoughtworks/dsl/Dsl.scala index 05d88db11..e9b14c00d 100644 --- a/Dsl/src/main/scala/com/thoughtworks/dsl/Dsl.scala +++ b/Dsl/src/main/scala/com/thoughtworks/dsl/Dsl.scala @@ -408,130 +408,6 @@ object Dsl extends LowPriorityDsl0 { } } - @implicitNotFound( - "The `try` ... `catch` expression cannot contain !-notation inside a function that returns ${OuterDomain}." - ) - @deprecated( - "Dsl.TryCatch / Dsl.TryFinally / Dsl.TryCatchFinally will be removed", - "2.0.0" - ) - trait TryCatch[Value, OuterDomain, BlockDomain] { - def tryCatch( - block: BlockDomain !! Value, - catcher: Catcher[BlockDomain !! Value], - outerSuccessHandler: Value => OuterDomain - ): OuterDomain - } - - private[dsl] trait LowPriorityTryCatch { - 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) - } - - restTryCatch.tryCatch( - withState(block), - catcher.andThen(withState _), - outerSuccessHandler(_)(state) - ) - } - } - - object TryCatch extends LowPriorityTryCatch { - - implicit def throwableContinuationTryCatch[LeftDomain, Value] - : TryCatch[Value, LeftDomain !! Throwable, LeftDomain !! Throwable] = { - - ( - block: LeftDomain !! Throwable !! Value, - catcher: Catcher[LeftDomain !! Throwable !! Value], - outerSuccessHandler: Value => LeftDomain !! Throwable - ) => outerFailureHandler => - def innerFailureHandler(e: Throwable): LeftDomain = { - catcher.lift(e) match { - case None => - outerFailureHandler(e) - case Some(recovered) => - @inline - def recoveredHandler(): LeftDomain = { - locally { - try { - recovered(outerSuccessHandler) - } catch { - case NonFatal(nativeThrown) => - return outerFailureHandler(nativeThrown) - } - }(outerFailureHandler) - } - - recoveredHandler() - } - } - - def runBlock(): LeftDomain = { - (try { - block { a => hookedFailureHandler => - @inline - def successHandler(): LeftDomain = { - locally { - try { - outerSuccessHandler(a) - } catch { - case NonFatal(nativeThrown) => - return outerFailureHandler(nativeThrown) - } - }(outerFailureHandler) - } - - successHandler() - } - } catch { - case NonFatal(e) => - return innerFailureHandler(e) - })(innerFailureHandler) - } - runBlock() - } - - implicit def futureTryCatch[BlockValue, OuterValue](implicit - executionContext: ExecutionContext - ): TryCatch[BlockValue, Future[OuterValue], Future[BlockValue]] = { - ( - block: Future[BlockValue] !! BlockValue, - catcher: Catcher[Future[BlockValue] !! BlockValue], - outerSuccessHandler: BlockValue => Future[OuterValue] - ) => - catchNativeException(block) - .recoverWith { case e: Throwable => - def recover(): Future[BlockValue] = { - (try { - catcher.lift(e) - } catch { - case NonFatal(extractorException) => - return Future.failed(extractorException) - }) match { - case None => - Future.failed(e) - case Some(recovered) => - catchNativeException(recovered) - } - } - recover() - } - .flatMap(outerSuccessHandler) - } - } - @implicitNotFound( "The `try` ... `finally` expression cannot contain !-notation inside a function that returns ${OuterDomain}." ) diff --git a/build.sbt b/build.sbt index 0c0fd62b7..a7ecea2ae 100644 --- a/build.sbt +++ b/build.sbt @@ -186,6 +186,8 @@ lazy val `domains-scalaz` = reset % Test, `keywords-Monadic`, `keywords-Return`, + `keywords-TryCatch`, + `keywords-TryFinally`, `keywords-Shift` % Test, `keywords-Yield` % Test ) 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 42ab1f6b8..228370d1d 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 @@ -1,4 +1,5 @@ -package com.thoughtworks.dsl.domains +package com.thoughtworks.dsl +package domains import com.thoughtworks.dsl.Dsl import com.thoughtworks.dsl.Dsl.!! @@ -7,7 +8,7 @@ import scala.language.higherKinds import scala.language.implicitConversions import _root_.scalaz.{Applicative, Bind, Monad, MonadError, MonadTrans} import com.thoughtworks.dsl.keywords.{Monadic, Return} -import com.thoughtworks.dsl.Dsl.{TryCatch, TryFinally} +import com.thoughtworks.dsl.Dsl.{TryFinally} import scala.util.control.Exception.Catcher import scala.util.control.NonFatal @@ -145,7 +146,6 @@ object scalaz extends scalaz.LowPriority0 { monadThrowable.raiseError(e) } } - implicit def scalazTryFinally[F[_], A, B](implicit monadError: MonadThrowable[F] ): TryFinally[A, F[B], F[A], F[Unit]] = @@ -172,36 +172,36 @@ object scalaz extends scalaz.LowPriority0 { } } - implicit def scalazTryCatch[F[_], A, B](implicit + given [F[_], A, B](using monadError: MonadThrowable[F] - ): TryCatch[A, F[B], F[A]] = - new TryCatch[A, F[B], F[A]] { - def tryCatch( - block: F[A] !! A, - catcher: Catcher[F[A] !! A], - outerSuccessHandler: A => F[B] - ): F[B] = { - import monadError.monadErrorSyntax._ - catchNativeException(block) - .handleError { e => - def recover(): F[A] = { - (try { - catcher.lift(e) - } catch { - case NonFatal(extractorException) => - return monadError.raiseError(extractorException) - }) match { - case None => - monadError.raiseError(e) - case Some(recovered) => - catchNativeException(recovered) - } - } - recover() - } - .flatMap(outerSuccessHandler) - } + ): keywords.TryCatch.DslComposer[F[B], A, F[A]] = + keywords.TryCatch.DslComposer { + [BlockKeyword, CaseKeyword] => + ( + blockDsl: Dsl.Searching[BlockKeyword, F[A], A], + caseDsl: Dsl.Searching[CaseKeyword, F[A], A] + ) ?=> + Dsl.Composed[keywords.TryCatch[BlockKeyword, CaseKeyword], F[B], A] { + case (keywords.TryCatch(block, catcher), outerSuccessHandler) => + import monadError.monadErrorSyntax._ + val blockDomain = + try { + summon[Dsl.Run[BlockKeyword, F[A], A]](block()) + } catch { + case NonFatal(e) => + monadError.raiseError(e) + } + blockDomain + .handleError { + case catcher(recovered) => + summon[Dsl.Run[CaseKeyword, F[A], A]](recovered) + case e: Throwable => + throw e + } + .flatMap(outerSuccessHandler) + } } + private[scalaz] trait LowPriority0: /** The [[Dsl]] instance that converts a keyword to the monad domain type * then flatMap. This instance helps when the keyword supports a domain `D` 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 99471f486..1a61c1e67 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 @@ -5,30 +5,208 @@ import Dsl.IsKeyword import scala.util.control.Exception.Catcher import scala.concurrent._ import scala.util.control.NonFatal +import com.thoughtworks.dsl.Dsl.cpsApply +import scala.annotation.implicitNotFound case class TryCatch[+BlockKeyword, +CaseKeyword]( block: () => BlockKeyword, cases: Catcher[CaseKeyword] ) extends Dsl.Keyword.Trait -object TryCatch extends TryCatch.LegacyInstances { - - trait LegacyInstances: - @deprecated( - "Dsl.TryCatch / Dsl.TryFinally / Dsl.TryCatchFinally will be removed", - "2.0.0" - ) - given [Value, OuterDomain, BlockKeyword, BlockDomain, CaseKeyword](using - dslTryCatch: Dsl.TryCatch[Value, OuterDomain, BlockDomain], - 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( - // TODO: Use Suspend to catch the exception - blockDsl(blockKeyword(), _), - cases.andThen { caseKeyword => caseDsl(caseKeyword, _) }, - handler - ) +object TryCatch extends TryCatch.LowPriority0 { + @implicitNotFound( + "The `try` ... `catch` expression cannot contain !-notation inside a function that returns ${OuterDomain}." + ) + opaque type DslComposer[OuterDomain, Value, BlockDomain] <: [ + BlockKeyword, + CaseKeyword + ] => ( + Dsl.Searching[BlockKeyword, BlockDomain, Value], + Dsl.Searching[CaseKeyword, BlockDomain, Value] + ) ?=> Dsl.Composed[TryCatch[BlockKeyword, CaseKeyword], OuterDomain, Value] = + [BlockKeyword, CaseKeyword] => ( + Dsl.Searching[BlockKeyword, BlockDomain, Value], + Dsl.Searching[CaseKeyword, BlockDomain, Value] + ) ?=> Dsl.Composed[TryCatch[BlockKeyword, CaseKeyword], OuterDomain, Value] + object DslComposer: + def apply[OuterDomain, Value, BlockDomain]: ( + [ + BlockKeyword, + CaseKeyword + ] => ( + Dsl.Searching[BlockKeyword, BlockDomain, Value], + Dsl.Searching[CaseKeyword, BlockDomain, Value] + ) ?=> Dsl.Composed[TryCatch[ + BlockKeyword, + CaseKeyword + ], OuterDomain, Value] + ) =:= DslComposer[OuterDomain, Value, BlockDomain] = summon + + private[TryCatch] trait LowPriority0: + given [ + State, + OuterDomain, + Value, + BlockDomain + ](using + restComposer: DslComposer[OuterDomain, Value, BlockDomain] + ): DslComposer[State => OuterDomain, Value, State => BlockDomain] = + DslComposer { + [BlockKeyword, CaseKeyword] => + ( + blockDsl: Dsl.Searching[ + BlockKeyword, + State => BlockDomain, + Value + ], + caseDsl: Dsl.Searching[CaseKeyword, State => BlockDomain, Value] + ) ?=> + Dsl.Composed[TryCatch[ + BlockKeyword, + CaseKeyword + ], State => OuterDomain, Value] { (keyword, outerSuccessHandler) => + val TryCatch(block, catcher) = keyword + (state: State) => + def withState[Keyword](keyword: Keyword)(using + Dsl.Searching[Keyword, State => BlockDomain, Value] + ): Shift[BlockDomain, Value] = Shift { + (blockHandler: (Value => BlockDomain)) => + keyword.cpsApply { (value: Value) => (state: State) => + blockHandler(value) + }(state) + } + restComposer[Shift[BlockDomain, Value], Shift[ + BlockDomain, + Value + ]]( + TryCatch( + () => withState[BlockKeyword](block()), + { case catcher(caseKeyword) => + withState[CaseKeyword](caseKeyword) + } + ), + outerSuccessHandler(_)(state) + ) + } } + given [ + LeftDomain, + Value + ]: DslComposer[LeftDomain !! Throwable, Value, LeftDomain !! Throwable] = + DslComposer { + [BlockKeyword, CaseKeyword] => + ( + blockDsl: Dsl.Searching[ + BlockKeyword, + LeftDomain !! Throwable, + Value + ], + caseDsl: Dsl.Searching[ + CaseKeyword, + LeftDomain !! Throwable, + Value + ] + ) ?=> + Dsl.Composed[TryCatch[ + BlockKeyword, + CaseKeyword + ], LeftDomain !! Throwable, Value] { + case ( + TryCatch(block, catcher), + outerSuccessHandler: (Value => LeftDomain !! Throwable) + ) => + outerFailureHandler => + // TODO: Simplify the implementation. We should allow it to throw native unhandled exceptions instead of handling it, assuming there will be another exception handler for it + def innerFailureHandler(e: Throwable): LeftDomain = { + catcher.lift(e) match { + case None => + outerFailureHandler(e) + case Some(recovered) => + @inline + def recoveredHandler(): LeftDomain = { + locally { + try { + recovered.cpsApply(outerSuccessHandler) + } catch { + case NonFatal(nativeThrown) => + return outerFailureHandler(nativeThrown) + } + }(outerFailureHandler) + } + recoveredHandler() + } + } + + def runBlock(): LeftDomain = { + (try { + block().cpsApply { a => hookedFailureHandler => + @inline + def successHandler(): LeftDomain = { + locally { + try { + outerSuccessHandler(a) + } catch { + case NonFatal(nativeThrown) => + return outerFailureHandler(nativeThrown) + } + }(outerFailureHandler) + } + + successHandler() + } + } catch { + case NonFatal(e) => + return innerFailureHandler(e) + })(innerFailureHandler) + } + runBlock() + } + } + + given [BlockValue, OuterValue](using ExecutionContext): DslComposer[ + Future[OuterValue], + BlockValue, + Future[BlockValue] + ] = DslComposer { + [BlockKeyword, CaseKeyword] => + ( + blockDsl: Dsl.Searching[BlockKeyword, Future[ + BlockValue + ], BlockValue], + caseDsl: Dsl.Searching[CaseKeyword, Future[BlockValue], BlockValue] + ) ?=> + Dsl.Composed[TryCatch[BlockKeyword, CaseKeyword], Future[ + OuterValue + ], BlockValue] { + case ( + TryCatch(block, catcher), + outerSuccessHandler: (BlockValue => Future[OuterValue]) + ) => + val blockFuture = + try { + summon[Dsl.Run[BlockKeyword, Future[BlockValue], BlockValue]]( + block() + ) + } catch { + case NonFatal(e) => + Future.failed(e) + } + blockFuture + .recoverWith { case catcher(recovered) => + summon[ + Dsl.Run[CaseKeyword, Future[BlockValue], BlockValue] + ](recovered) + } + .flatMap(outerSuccessHandler) + } + } + + given [Value, OuterDomain, BlockKeyword, BlockDomain, CaseKeyword](using + dslTryCatch: TryCatch.DslComposer[OuterDomain, Value, BlockDomain] + )(using + Dsl.Searching[BlockKeyword, BlockDomain, Value], + Dsl.Searching[CaseKeyword, BlockDomain, Value] + ): Dsl.Composed[TryCatch[BlockKeyword, CaseKeyword], OuterDomain, Value] = + dslTryCatch[BlockKeyword, CaseKeyword] + } From d977b3eea918d8278bb0b89be5a19a7b02b9d502 Mon Sep 17 00:00:00 2001 From: "Yang, Bo" Date: Fri, 24 Dec 2021 01:37:04 -0800 Subject: [PATCH 7/7] Reimplement the Dsl for keywords.TryFinally based on TryCatch instead of Dsl.TryFinally --- .../main/scala/com/thoughtworks/dsl/Dsl.scala | 139 ------------------ build.sbt | 2 +- .../com/thoughtworks/dsl/domains/scalaz.scala | 26 ---- .../dsl/keywords/TryFinally.scala | 60 +++++--- .../com/thoughtworks/dsl/keywords/Using.scala | 23 +-- 5 files changed, 54 insertions(+), 196 deletions(-) diff --git a/Dsl/src/main/scala/com/thoughtworks/dsl/Dsl.scala b/Dsl/src/main/scala/com/thoughtworks/dsl/Dsl.scala index e9b14c00d..f77aea9d7 100644 --- a/Dsl/src/main/scala/com/thoughtworks/dsl/Dsl.scala +++ b/Dsl/src/main/scala/com/thoughtworks/dsl/Dsl.scala @@ -397,145 +397,6 @@ object Dsl extends LowPriorityDsl0 { private[dsl] type !![R, +A] = (A => R) => R - private def catchNativeException[A]( - futureContinuation: Future[A] !! A - ): Future[A] = { - try { - futureContinuation(Future.successful) - } catch { - case NonFatal(e) => - Future.failed(e) - } - } - - @implicitNotFound( - "The `try` ... `finally` expression cannot contain !-notation inside a function that returns ${OuterDomain}." - ) - @deprecated( - "Dsl.TryCatch / Dsl.TryFinally / Dsl.TryCatchFinally will be removed", - "2.0.0" - ) - trait TryFinally[Value, OuterDomain, BlockDomain, FinalizerDomain] { - def tryFinally( - block: BlockDomain !! Value, - finalizer: FinalizerDomain !! Unit, - outerSuccessHandler: Value => OuterDomain - ): OuterDomain - } - - 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 - ] = { - ( - 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) - } - - restTryFinally.tryFinally( - withState(block), - withState(finalizer), - outerSuccessHandler(_)(state) - ) - } - } - - object TryFinally extends LowPriorityTryFinally { - - implicit def futureTryFinally[BlockValue, OuterValue](implicit - executionContext: ExecutionContext - ): TryFinally[BlockValue, Future[OuterValue], Future[BlockValue], Future[ - Unit - ]] = { - ( - block: Future[BlockValue] !! BlockValue, - finalizer: Future[Unit] !! Unit, - outerSuccessHandler: BlockValue => Future[OuterValue] - ) => - @inline - def injectFinalizer[A](f: Unit => Future[A]): Future[A] = { - catchNativeException(finalizer).flatMap(f) - } - - catchNativeException(block) - .recoverWith { case e: Throwable => - injectFinalizer { (_: Unit) => - Future.failed(e) - } - } - .flatMap { a => - injectFinalizer { (_: Unit) => - outerSuccessHandler(a) - } - } - } - - 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) - } - } - } catch { - case NonFatal(e) => - return hookedFailureHandler(e) - })(hookedFailureHandler) - } - runBlock() - } - } - @FunctionalInterface trait Lift[From, +To] extends (From => To) private[dsl] trait LowPriorityLift0 { this: Lift.type => diff --git a/build.sbt b/build.sbt index a7ecea2ae..054c3414f 100644 --- a/build.sbt +++ b/build.sbt @@ -77,7 +77,7 @@ lazy val `keywords-TryCatchFinally` = lazy val `keywords-TryFinally` = crossProject(JSPlatform, JVMPlatform) .crossType(CrossType.Pure) - .dependsOn(Dsl, `keywords-Shift`, `keywords-Match`) + .dependsOn(Dsl, `keywords-TryCatch`) lazy val `keywords-While` = crossProject(JSPlatform, JVMPlatform) 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 228370d1d..005a84f9d 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 @@ -8,7 +8,6 @@ import scala.language.higherKinds import scala.language.implicitConversions import _root_.scalaz.{Applicative, Bind, Monad, MonadError, MonadTrans} import com.thoughtworks.dsl.keywords.{Monadic, Return} -import com.thoughtworks.dsl.Dsl.{TryFinally} import scala.util.control.Exception.Catcher import scala.util.control.NonFatal @@ -146,31 +145,6 @@ object scalaz extends scalaz.LowPriority0 { monadThrowable.raiseError(e) } } - implicit def scalazTryFinally[F[_], A, B](implicit - monadError: MonadThrowable[F] - ): TryFinally[A, F[B], F[A], F[Unit]] = - new TryFinally[A, F[B], F[A], F[Unit]] { - def tryFinally( - block: F[A] !! A, - finalizer: F[Unit] !! Unit, - outerSuccessHandler: A => F[B] - ): F[B] = { - @inline - def injectFinalizer[A](f: Unit => F[A]): F[A] = { - monadError.bind(catchNativeException(finalizer))(f) - } - monadError.bind(monadError.handleError(catchNativeException(block)) { - (e: Throwable) => - injectFinalizer { (_: Unit) => - monadError.raiseError(e) - } - }) { a => - injectFinalizer { (_: Unit) => - outerSuccessHandler(a) - } - } - } - } given [F[_], A, B](using monadError: MonadThrowable[F] 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 8df8f5872..bb2324531 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 @@ -5,30 +5,52 @@ import Dsl.IsKeyword import scala.util.control.Exception.Catcher import scala.concurrent._ import scala.util.control.NonFatal +import scala.util.Success +import scala.util.Failure +import scala.util.Try case class TryFinally[+TryKeyword, +FinalizerKeyword]( block: () => TryKeyword, finalizer: () => FinalizerKeyword ) extends Dsl.Keyword.Trait -object TryFinally extends TryFinally.LegacyInstances { - trait LegacyInstances: - @deprecated( - "Dsl.TryCatch / Dsl.TryFinally / Dsl.TryCatchFinally will be removed", - "2.0.0" - ) - given [Value, OuterDomain, BlockKeyword, BlockDomain, FinalizerKeyword, FinalizerDomain](using - dslTryFinally: Dsl.TryFinally[Value, OuterDomain, BlockDomain, FinalizerDomain], - 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( - // TODO: Use Suspend to catch the exception - blockDsl(blockKeyword(), _), - finalizerDsl(finalizerKeyword(), _), - handler - ) - } +object TryFinally { + type DslComposer[OuterDomain, Value, BlockDomain] = + TryCatch.DslComposer[OuterDomain, Try[Value], BlockDomain] + + given [ + Value, + OuterDomain, + BlockKeyword, + BlockDomain, + FinalizerKeyword + ](using + DslComposer[OuterDomain, Value, BlockDomain], + Dsl.Searching[BlockKeyword, BlockDomain, Value], + Dsl.Searching[FinalizerKeyword, OuterDomain, Unit] + ): Dsl.Composed[TryFinally[ + BlockKeyword, + FinalizerKeyword + ], OuterDomain, Value] = Dsl.Composed { + case (TryFinally(blockKeyword, finalizerKeyword), handler) => + val transformedAst = FlatMap( + TryCatch( + () => + FlatMap( + blockKeyword(), + (successValue: Value) => Pure(Success(successValue)) + ), + { case NonFatal(e) => + Pure(Failure[Value](e)) + } + ), + (result: Try[Value]) => + FlatMap(finalizerKeyword(), (_: Unit) => Pure(result.get)) + ) + summon[Dsl.Searching[transformedAst.type, OuterDomain, Value]]( + transformedAst, + handler + ) + } } 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 c282308f4..0fa261714 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 @@ -84,17 +84,18 @@ object Using { FinalizerDomain ](using IsKeyword[Mapped, MappedValue], - Dsl.TryFinally[MappedValue, OuterDomain, BlockDomain, FinalizerDomain], + TryFinally.DslComposer[OuterDomain, MappedValue, BlockDomain], 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[OuterDomain] { - handler(try { - !flatMapper(r) - } finally { - r.close() - }) - } - } + ): Dsl.Composed[FlatMap[Using[R], Mapped], OuterDomain, MappedValue] = + Dsl.Composed { + case (FlatMap(r, flatMapper: (Using[R] @unchecked => Mapped)), handler) => + reset[OuterDomain] { + handler(try { + !flatMapper(r) + } finally { + r.close() + }) + } + } }