Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert "Revert "Simplify Using Dsl with the help of FlatMap"" #517

Merged
merged 1 commit into from
Dec 11, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,122 +1,94 @@
package com.thoughtworks.dsl
package keywords

import com.thoughtworks.dsl.bangnotation.{ `*`, reify, reset, unary_!}
import com.thoughtworks.dsl.bangnotation.{`*`, reify, reset, unary_!}
import com.thoughtworks.dsl.Dsl
import com.thoughtworks.dsl.Dsl.!!
import com.thoughtworks.dsl.Dsl.AsKeyword
// import com.thoughtworks.dsl.keywords.Catch.{CatchDsl, DslCatch}
import com.thoughtworks.dsl.keywords.TryFinally
import com.thoughtworks.dsl.Dsl.cpsApply

import scala.concurrent.{ExecutionContext, Future}
import scala.language.implicitConversions
import scala.util.control.NonFatal

/** This [[Using]] keyword automatically manage resources in [[scala.concurrent.Future]], [[domains.task.Task]],
* and other asynchronous domains derived from `Future` or `Task`.
/** This [[Using]] keyword automatically manage resources in
* [[scala.concurrent.Future]], [[domains.task.Task]], and other asynchronous
* domains derived from `Future` or `Task`.
*
* @author 杨博 (Yang Bo)
* @see [[dsl]] for usage of this [[Using]] keyword in continuations
* @author
* 杨博 (Yang Bo)
* @see
* [[dsl]] for usage of this [[Using]] keyword in continuations
*/
final case class Using[R <: AutoCloseable](open: () => R) extends AnyVal
opaque type Using[R <: AutoCloseable] = R

object Using {
given [R <: AutoCloseable]: AsKeyword.IsKeyword[Using[R], R] with {}

given [R <: AutoCloseable]: AsKeyword[R, Using[R], R] = r => Using(() => r)
given [R <: AutoCloseable]: AsKeyword[R, Using[R], R] = Using(_)

trait ScopeExitHandler extends AutoCloseable

/** Returns a [[Using]] keyword to execute a [[ScopeExitHandler]] when exiting the nearest enclosing scope
* that is annotated as [[Dsl.reset @reset]],
* (or the nearest enclosing function if [[compilerplugins.ResetEverywhere]] is enabled).
/** Returns a [[Using]] keyword to execute a [[ScopeExitHandler]] when exiting
* the nearest enclosing scope that is annotated as [[Dsl.reset @reset]], (or
* the nearest enclosing function if [[compilerplugins.ResetEverywhere]] is
* enabled).
*
* @note This method is similar to [[apply]],
* except the parameter type is changed from a generic `R` to the SAM type [[ScopeExitHandler]],
* which allows for function literal expressions
* in Scala 2.12+ or Scala 2.11 with `-Xexperimental` compiler option.
* @note
* This method is similar to [[apply]], except the parameter type is
* changed from a generic `R` to the SAM type [[ScopeExitHandler]], which
* allows for function literal expressions in Scala 2.12+ or Scala 2.11
* with `-Xexperimental` compiler option.
*
* @example The following function will perform `n *= 2` after `n += 20`:
* @example
* The following function will perform `n *= 2` after `n += 20`:
*
* {{{
* import scala.concurrent.Future
* import com.thoughtworks.dsl.keywords.Using.scopeExit
* import com.thoughtworks.dsl.bangnotation._
* var n = 1
* def multiplicationAfterAddition = *[Future] {
* !scopeExit { () =>
* n *= 2
* }
* n += 20
* }
* }}}
* {{{
* import scala.concurrent.Future
* import com.thoughtworks.dsl.keywords.Using.scopeExit
* import com.thoughtworks.dsl.bangnotation._
* var n = 1
* def multiplicationAfterAddition = *[Future] {
* !scopeExit { () =>
* n *= 2
* }
* n += 20
* }
* }}}
*
* Therefore, the final value of `n` should be `(1 + 20) * 2 = 42`.
* Therefore, the final value of `n` should be `(1 + 20) * 2 = 42`.
*
* {{{
* multiplicationAfterAddition.map { _ =>
* n should be(42)
* }
* }}}
* {{{
* multiplicationAfterAddition.map { _ =>
* n should be(42)
* }
* }}}
*/
def scopeExit(r: => ScopeExitHandler) = new Using(() => r)
def scopeExit(r: ScopeExitHandler) = r

def apply[R <: AutoCloseable](r: => R)(implicit
dummyImplicit: DummyImplicit = DummyImplicit.dummyImplicit
): Using[R] = new Using(() => r)
def apply[R <: AutoCloseable]: R =:= Using[R] = summon

implicit def continuationUsingDsl[Domain, Value, R <: AutoCloseable](implicit
tryFinally: Dsl.TryFinally[Value, Domain, Domain, Domain],
// shiftDsl: Dsl[Shift[Domain, Value], Domain, Value]
): Dsl[Using[R], Domain !! Value, R] = { (keyword: Using[R], handler: R => Domain !! Value) =>
*[[X] =>> Domain !! X] {
val r = keyword.open()
try {
!Shift[Domain, Value](handler(r))
} finally {
r.close()
}
}
}

implicit def scalaFutureUsingDsl[R <: AutoCloseable, A](implicit executionContext: ExecutionContext)
: Dsl[Using[R], Future[A], R] = { (keyword: Using[R], handler: R => Future[A]) =>
Future(keyword.open()).flatMap { (r: R) =>
def onFailure(e: Throwable): Future[Nothing] = {
try {
given [
R <: AutoCloseable,
Mapped,
MappedValue,
OuterDomain,
BlockDomain,
FinalizerDomain
](using
AsKeyword.IsKeyword[Mapped, MappedValue],
Dsl.TryFinally[MappedValue, OuterDomain, BlockDomain, FinalizerDomain],
Dsl.PolyCont[Mapped, BlockDomain, MappedValue]
): Dsl.PolyCont[FlatMap[Using[R], R, Mapped], OuterDomain, MappedValue] = {
case (FlatMap(r, flatMapper), handler) =>
reset {
handler(try {
!flatMapper(r)
} finally {
r.close()
Future.failed(e)
} catch {
case NonFatal(e2) =>
Future.failed(e2)
}
})
}

def onSuccess(a: A): Future[A] = {
try {
r.close()
Future.successful(a)
} catch {
case NonFatal(e2) =>
Future.failed(e2)
}
}

def returnableBlock(): Future[A] = {
val fa: Future[A] = try {
handler(r)
} catch {
case NonFatal(e) =>
return onFailure(e)
}
fa.recoverWith {
case NonFatal(e) =>
onFailure(e)
}
.flatMap(onSuccess)
}
returnableBlock()
}
}

}