|
1 | 1 | package com.thoughtworks.dsl |
2 | 2 | package keywords |
3 | 3 |
|
4 | | -import com.thoughtworks.dsl.bangnotation.{`*`, reify, reset, unary_!} |
| 4 | +import com.thoughtworks.dsl.bangnotation.{ `*`, reify, reset, unary_!} |
5 | 5 | import com.thoughtworks.dsl.Dsl |
6 | 6 | import com.thoughtworks.dsl.Dsl.!! |
7 | 7 | import com.thoughtworks.dsl.Dsl.AsKeyword |
| 8 | +// import com.thoughtworks.dsl.keywords.Catch.{CatchDsl, DslCatch} |
8 | 9 | import com.thoughtworks.dsl.keywords.TryFinally |
9 | 10 | import com.thoughtworks.dsl.Dsl.cpsApply |
10 | 11 |
|
11 | 12 | import scala.concurrent.{ExecutionContext, Future} |
12 | 13 | import scala.language.implicitConversions |
13 | 14 | import scala.util.control.NonFatal |
14 | 15 |
|
15 | | -/** This [[Using]] keyword automatically manage resources in |
16 | | - * [[scala.concurrent.Future]], [[domains.task.Task]], and other asynchronous |
17 | | - * domains derived from `Future` or `Task`. |
| 16 | +/** This [[Using]] keyword automatically manage resources in [[scala.concurrent.Future]], [[domains.task.Task]], |
| 17 | + * and other asynchronous domains derived from `Future` or `Task`. |
18 | 18 | * |
19 | | - * @author |
20 | | - * 杨博 (Yang Bo) |
21 | | - * @see |
22 | | - * [[dsl]] for usage of this [[Using]] keyword in continuations |
| 19 | + * @author 杨博 (Yang Bo) |
| 20 | + * @see [[dsl]] for usage of this [[Using]] keyword in continuations |
23 | 21 | */ |
24 | | -opaque type Using[R <: AutoCloseable] = R |
| 22 | +final case class Using[R <: AutoCloseable](open: () => R) extends AnyVal |
25 | 23 |
|
26 | 24 | object Using { |
27 | 25 | given [R <: AutoCloseable]: AsKeyword.IsKeyword[Using[R], R] with {} |
28 | 26 |
|
29 | | - given [R <: AutoCloseable]: AsKeyword[R, Using[R], R] = Using(_) |
| 27 | + given [R <: AutoCloseable]: AsKeyword[R, Using[R], R] = r => Using(() => r) |
30 | 28 |
|
31 | 29 | trait ScopeExitHandler extends AutoCloseable |
32 | 30 |
|
33 | | - /** Returns a [[Using]] keyword to execute a [[ScopeExitHandler]] when exiting |
34 | | - * the nearest enclosing scope that is annotated as [[Dsl.reset @reset]], (or |
35 | | - * the nearest enclosing function if [[compilerplugins.ResetEverywhere]] is |
36 | | - * enabled). |
| 31 | + /** Returns a [[Using]] keyword to execute a [[ScopeExitHandler]] when exiting the nearest enclosing scope |
| 32 | + * that is annotated as [[Dsl.reset @reset]], |
| 33 | + * (or the nearest enclosing function if [[compilerplugins.ResetEverywhere]] is enabled). |
37 | 34 | * |
38 | | - * @note |
39 | | - * This method is similar to [[apply]], except the parameter type is |
40 | | - * changed from a generic `R` to the SAM type [[ScopeExitHandler]], which |
41 | | - * allows for function literal expressions in Scala 2.12+ or Scala 2.11 |
42 | | - * with `-Xexperimental` compiler option. |
| 35 | + * @note This method is similar to [[apply]], |
| 36 | + * except the parameter type is changed from a generic `R` to the SAM type [[ScopeExitHandler]], |
| 37 | + * which allows for function literal expressions |
| 38 | + * in Scala 2.12+ or Scala 2.11 with `-Xexperimental` compiler option. |
43 | 39 | * |
44 | | - * @example |
45 | | - * The following function will perform `n *= 2` after `n += 20`: |
| 40 | + * @example The following function will perform `n *= 2` after `n += 20`: |
46 | 41 | * |
47 | | - * {{{ |
48 | | - * import scala.concurrent.Future |
49 | | - * import com.thoughtworks.dsl.keywords.Using.scopeExit |
50 | | - * import com.thoughtworks.dsl.bangnotation._ |
51 | | - * var n = 1 |
52 | | - * def multiplicationAfterAddition = *[Future] { |
53 | | - * !scopeExit { () => |
54 | | - * n *= 2 |
55 | | - * } |
56 | | - * n += 20 |
57 | | - * } |
58 | | - * }}} |
| 42 | + * {{{ |
| 43 | + * import scala.concurrent.Future |
| 44 | + * import com.thoughtworks.dsl.keywords.Using.scopeExit |
| 45 | + * import com.thoughtworks.dsl.bangnotation._ |
| 46 | + * var n = 1 |
| 47 | + * def multiplicationAfterAddition = *[Future] { |
| 48 | + * !scopeExit { () => |
| 49 | + * n *= 2 |
| 50 | + * } |
| 51 | + * n += 20 |
| 52 | + * } |
| 53 | + * }}} |
59 | 54 | * |
60 | | - * Therefore, the final value of `n` should be `(1 + 20) * 2 = 42`. |
| 55 | + * Therefore, the final value of `n` should be `(1 + 20) * 2 = 42`. |
61 | 56 | * |
62 | | - * {{{ |
63 | | - * multiplicationAfterAddition.map { _ => |
64 | | - * n should be(42) |
65 | | - * } |
66 | | - * }}} |
| 57 | + * {{{ |
| 58 | + * multiplicationAfterAddition.map { _ => |
| 59 | + * n should be(42) |
| 60 | + * } |
| 61 | + * }}} |
67 | 62 | */ |
68 | | - def scopeExit(r: ScopeExitHandler) = r |
| 63 | + def scopeExit(r: => ScopeExitHandler) = new Using(() => r) |
69 | 64 |
|
70 | | - def apply[R <: AutoCloseable]: R =:= Using[R] = summon |
| 65 | + def apply[R <: AutoCloseable](r: => R)(implicit |
| 66 | + dummyImplicit: DummyImplicit = DummyImplicit.dummyImplicit |
| 67 | + ): Using[R] = new Using(() => r) |
71 | 68 |
|
72 | | - given [ |
73 | | - R <: AutoCloseable, |
74 | | - Mapped, |
75 | | - MappedValue, |
76 | | - OuterDomain, |
77 | | - BlockDomain, |
78 | | - FinalizerDomain |
79 | | - ](using |
80 | | - AsKeyword.IsKeyword[Mapped, MappedValue], |
81 | | - Dsl.TryFinally[MappedValue, OuterDomain, BlockDomain, FinalizerDomain], |
82 | | - Dsl.PolyCont[Mapped, BlockDomain, MappedValue] |
83 | | - ): Dsl.PolyCont[FlatMap[Using[R], R, Mapped], OuterDomain, MappedValue] = { |
84 | | - case (FlatMap(r, flatMapper), handler) => |
85 | | - reset { |
86 | | - handler(try { |
87 | | - !flatMapper(r) |
88 | | - } finally { |
89 | | - r.close() |
90 | | - }) |
| 69 | + implicit def continuationUsingDsl[Domain, Value, R <: AutoCloseable](implicit |
| 70 | + tryFinally: Dsl.TryFinally[Value, Domain, Domain, Domain], |
| 71 | + // shiftDsl: Dsl[Shift[Domain, Value], Domain, Value] |
| 72 | + ): Dsl[Using[R], Domain !! Value, R] = { (keyword: Using[R], handler: R => Domain !! Value) => |
| 73 | + *[[X] =>> Domain !! X] { |
| 74 | + val r = keyword.open() |
| 75 | + try { |
| 76 | + !Shift[Domain, Value](handler(r)) |
| 77 | + } finally { |
| 78 | + r.close() |
91 | 79 | } |
| 80 | + } |
92 | 81 | } |
93 | 82 |
|
| 83 | + implicit def scalaFutureUsingDsl[R <: AutoCloseable, A](implicit executionContext: ExecutionContext) |
| 84 | + : Dsl[Using[R], Future[A], R] = { (keyword: Using[R], handler: R => Future[A]) => |
| 85 | + Future(keyword.open()).flatMap { (r: R) => |
| 86 | + def onFailure(e: Throwable): Future[Nothing] = { |
| 87 | + try { |
| 88 | + r.close() |
| 89 | + Future.failed(e) |
| 90 | + } catch { |
| 91 | + case NonFatal(e2) => |
| 92 | + Future.failed(e2) |
| 93 | + } |
| 94 | + } |
| 95 | + |
| 96 | + def onSuccess(a: A): Future[A] = { |
| 97 | + try { |
| 98 | + r.close() |
| 99 | + Future.successful(a) |
| 100 | + } catch { |
| 101 | + case NonFatal(e2) => |
| 102 | + Future.failed(e2) |
| 103 | + } |
| 104 | + } |
| 105 | + |
| 106 | + def returnableBlock(): Future[A] = { |
| 107 | + val fa: Future[A] = try { |
| 108 | + handler(r) |
| 109 | + } catch { |
| 110 | + case NonFatal(e) => |
| 111 | + return onFailure(e) |
| 112 | + } |
| 113 | + fa.recoverWith { |
| 114 | + case NonFatal(e) => |
| 115 | + onFailure(e) |
| 116 | + } |
| 117 | + .flatMap(onSuccess) |
| 118 | + } |
| 119 | + returnableBlock() |
| 120 | + } |
| 121 | + } |
94 | 122 | } |
0 commit comments