@@ -42,8 +42,17 @@ trait MonadError[F[_]] {
42
42
case Failure (e) => error(e)
43
43
}
44
44
45
+ /** Deprecated method which doesn't work properly when constructing the `f` effect itself throws exceptions - the
46
+ * finalizer `e` is not run in that case. Use `ensure2` instead, which uses a lazy-evaluated by-name parameter.
47
+ */
48
+ @ deprecated(message = " Use ensure2 for proper exception handling" , since = " 1.5.0" )
45
49
def ensure [T ](f : F [T ], e : => F [Unit ]): F [T ]
46
50
51
+ /** Runs `f`, and ensures that `e` is always run afterwards, regardless of the outcome. `e` is run even when `f`
52
+ * throws exceptions during construction of the effect.
53
+ */
54
+ def ensure2 [T ](f : => F [T ], e : => F [Unit ]): F [T ] = ensure(f, e)
55
+
47
56
def blocking [T ](t : => T ): F [T ] = eval(t)
48
57
}
49
58
@@ -62,7 +71,7 @@ object syntax {
62
71
def map [B ](f : A => B )(implicit ME : MonadError [F ]): F [B ] = ME .map(r)(f)
63
72
def flatMap [B ](f : A => F [B ])(implicit ME : MonadError [F ]): F [B ] = ME .flatMap(r)(f)
64
73
def handleError [T ](h : PartialFunction [Throwable , F [A ]])(implicit ME : MonadError [F ]): F [A ] = ME .handleError(r)(h)
65
- def ensure (e : => F [Unit ])(implicit ME : MonadError [F ]): F [A ] = ME .ensure (r, e)
74
+ def ensure (e : => F [Unit ])(implicit ME : MonadError [F ]): F [A ] = ME .ensure2 (r, e)
66
75
def flatTap [B ](f : A => F [B ])(implicit ME : MonadError [F ]): F [A ] = ME .flatTap(r)(f)
67
76
}
68
77
@@ -98,13 +107,24 @@ object EitherMonad extends MonadError[Either[Throwable, *]] {
98
107
case _ => rt
99
108
}
100
109
101
- override def ensure [T ](f : Either [Throwable , T ], e : => Either [Throwable , Unit ]): Either [Throwable , T ] = {
110
+ override def ensure [T ](f : Either [Throwable , T ], e : => Either [Throwable , Unit ]): Either [Throwable , T ] = ensure2(f, e)
111
+
112
+ override def ensure2 [T ](f : => Either [Throwable , T ], e : => Either [Throwable , Unit ]): Either [Throwable , T ] = {
102
113
def runE =
103
114
Try (e) match {
104
115
case Failure (f) => Left (f)
105
116
case Success (v) => v
106
117
}
107
- f match {
118
+
119
+ val ef =
120
+ try f
121
+ catch {
122
+ case t : Throwable =>
123
+ runE
124
+ throw t
125
+ }
126
+
127
+ ef match {
108
128
case Left (f) => runE.right.flatMap(_ => Left (f))
109
129
case Right (v) => runE.right.map(_ => v)
110
130
}
@@ -125,11 +145,22 @@ object TryMonad extends MonadError[Try] {
125
145
126
146
override def fromTry [T ](t : Try [T ]): Try [T ] = t
127
147
128
- override def ensure [T ](f : Try [T ], e : => Try [Unit ]): Try [T ] =
129
- f match {
148
+ override def ensure [T ](f : Try [T ], e : => Try [Unit ]): Try [T ] = ensure2(f, e)
149
+
150
+ override def ensure2 [T ](f : => Try [T ], e : => Try [Unit ]): Try [T ] = {
151
+ val ef =
152
+ try f
153
+ catch {
154
+ case t : Throwable =>
155
+ e
156
+ throw t
157
+ }
158
+
159
+ ef match {
130
160
case Success (v) => Try (e).flatten.map(_ => v)
131
161
case Failure (f) => Try (e).flatten.flatMap(_ => Failure (f))
132
162
}
163
+ }
133
164
}
134
165
class FutureMonad (implicit ec : ExecutionContext ) extends MonadAsyncError [Future ] {
135
166
override def unit [T ](t : T ): Future [T ] = Future .successful(t)
@@ -155,16 +186,23 @@ class FutureMonad(implicit ec: ExecutionContext) extends MonadAsyncError[Future]
155
186
p.future
156
187
}
157
188
158
- override def ensure [T ](f : Future [T ], e : => Future [Unit ]): Future [T ] = {
189
+ override def ensure [T ](f : Future [T ], e : => Future [Unit ]): Future [T ] = ensure2(f, e)
190
+
191
+ override def ensure2 [T ](f : => Future [T ], e : => Future [Unit ]): Future [T ] = {
159
192
val p = Promise [T ]()
160
193
def runE =
161
194
Try (e) match {
162
195
case Failure (f) => Future .failed(f)
163
196
case Success (v) => v
164
197
}
165
- f.onComplete {
166
- case Success (v) => runE.map(_ => v).onComplete(p.complete(_))
167
- case Failure (f) => runE.flatMap(_ => Future .failed(f)).onComplete(p.complete(_))
198
+ try {
199
+ f.onComplete {
200
+ case Success (v) => runE.map(_ => v).onComplete(p.complete(_))
201
+ case Failure (f) => runE.flatMap(_ => Future .failed(f)).onComplete(p.complete(_))
202
+ }
203
+ } catch {
204
+ case t : Throwable =>
205
+ e.onComplete(_ => p.complete(Failure (t)))
168
206
}
169
207
p.future
170
208
}
@@ -181,7 +219,8 @@ object IdentityMonad extends MonadError[Identity] {
181
219
h : PartialFunction [Throwable , Identity [T ]]
182
220
): Identity [T ] = rt
183
221
override def eval [T ](t : => T ): Identity [T ] = t
184
- override def ensure [T ](f : Identity [T ], e : => Identity [Unit ]): Identity [T ] =
222
+ override def ensure [T ](f : Identity [T ], e : => Identity [Unit ]): Identity [T ] = ensure2(f, e)
223
+ override def ensure2 [T ](f : => Identity [T ], e : => Identity [Unit ]): Identity [T ] =
185
224
try f
186
225
finally e
187
226
}
0 commit comments