-
Notifications
You must be signed in to change notification settings - Fork 29
/
Await.scala
150 lines (145 loc) · 4.41 KB
/
Await.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package com.thoughtworks.dsl
package keywords
import Dsl.IsKeyword
import com.thoughtworks.dsl.Dsl
import com.thoughtworks.dsl.domains.Continuation.!!
import scala.concurrent.Await.result
import scala.concurrent.duration.Duration
import scala.concurrent.{ExecutionContext, Future}
import scala.language.implicitConversions
/** [[Await]] is a [[Dsl.Keyword Keyword]] to extract value from a
* [[scala.concurrent.Future]].
*
* This keyword is available in functions whose return types are
* [[scala.concurrent.Future Future]], [[domains.task.Task]], or any exception
* aware continuations as `(_ !! Throwable !! _)`.
*
* @example
* Given a [[scala.concurrent.Future Future]]:
* {{{
* import com.thoughtworks.dsl.macros.Reset.Default.*
* import scala.concurrent.Future
* val myFuture40 = Future {
* 40
* }
* }}}
*
* You can [[Await]] the [[scala.concurrent.Future Future]] in another
* [[scala.concurrent.Future Future]]
*
* {{{
* def myFuture42 = *[Future] {
* !Await(myFuture40) + 2
* }
* }}}
*
* A [[scala.concurrent.Future Future]] can be converted to a
* [[domains.task.Task]] with the help of [[Await]].
*
* {{{
* import com.thoughtworks.dsl.domains.Task
* import com.thoughtworks.dsl.keywords.Await
* val myTask = Task {
* !Await(myFuture42)
* }
* }}}
*
* Then a [[domains.task.Task]] can be converted back to a
* [[scala.concurrent.Future]] via [[domains.task.Task.toFuture]].
*
* {{{
* val myAssertionTask = Task {
* !Shift(myTask) should be(42)
* }
* Task.toFuture(myAssertionTask)
* }}}
* @example
* `!Await` can be used together with `try` / `catch` / `finally`.
* {{{
* import scala.concurrent.Future
* import com.thoughtworks.dsl.macros.Reset.Default.*
* val buffer = new StringBuffer
* def recoverFuture = Future {
* buffer.append("Oh")
* }
* def exceptionalFuture = Future[StringBuffer] {
* throw new IllegalStateException("No")
* }
* def myFuture = *[Future] {
* try {
* !Await(exceptionalFuture)
* } catch {
* case e: IllegalStateException =>
* !Await(recoverFuture)
* buffer.append(' ')
* buffer.append(e.getMessage)
* } finally {
* buffer.append("!")
* }
* }
* myFuture.map(_.toString should be("Oh No!"))
* }}}
* @example
* Other keywords, including [[Return]] or [[Get]], can be used together with
* [[Await]]
* {{{
* import scala.concurrent.Future
* import com.thoughtworks.dsl.keywords.{Get, Return}
* import com.thoughtworks.dsl.macros.Reset.Default.*
* val buffer = new StringBuffer
* def recoverFuture = Future {
* buffer.append("Oh")
* }
* def exceptionalFuture = Future[StringBuffer] {
* throw new IllegalStateException("No")
* }
* def myFuture = reset[Char => Future[StringBuffer]](!Return {
* try {
* !Await(exceptionalFuture)
* } catch {
* case e: IllegalStateException =>
* !Await(recoverFuture)
* buffer.append(!Get[Char])
* buffer.append(e.getMessage)
* } finally {
* buffer.append("!")
* }
* })
* myFuture(' ').map(_.toString should be("Oh No!"))
* }}}
* @author
* 杨博 (Yang Bo)
*/
opaque type Await[+AwaitableValue] <: Dsl.Keyword.Opaque =
Dsl.Keyword.Opaque.Of[AwaitableValue]
@inline def Await[AwaitableValue](using
dummyImplicit: DummyImplicit = DummyImplicit.dummyImplicit
): AwaitableValue =:= Await[AwaitableValue] =
Dsl.Keyword.Opaque.Of
object Await extends AwaitJS {
given [FutureResult]: IsKeyword[Await[Future[FutureResult]], FutureResult]
with {}
given [FutureResult, That](using
ExecutionContext
): Dsl.Original[Await[Future[FutureResult]], Future[That], FutureResult] =
Dsl.Original(_ flatMap _)
// // TODO:
// implicit def tailRecContinuationAwaitDsl[Value](implicit
// executionContext: ExecutionContext
// ): Dsl.Original[Await[Value], TailRec[Unit] !! Throwable, Value]
given [Value](using
ExecutionContext
): Dsl.Original[Await[Future[Value]], Unit !! Throwable, Value] =
Dsl.Original {
(keyword: Await[Future[Value]], handler: Value => Unit !! Throwable) =>
!!.fromTryContinuation[Unit, Value](keyword.onComplete)(handler)
}
extension [FA, A](inline fa: FA)(using
inline notKeyword: util.NotGiven[
FA <:< Dsl.Keyword
],
inline asFA: FA <:< Future[A]
)
transparent inline def unary_! : A =
Dsl.shift(Await(asFA(fa))): A
}