Skip to content

Commit 28ad630

Browse files
committed
Enable try blocks for ValueTask
1 parent 626c95e commit 28ad630

File tree

3 files changed

+806
-15
lines changed

3 files changed

+806
-15
lines changed

src/FSharpPlus/Control/Monad.fs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ type TryWith =
217217
static member TryWith (computation: unit -> Async<_> , catchHandler: exn -> Async<_> , _: TryWith , _) = async.TryWith ((computation ()), catchHandler)
218218
#if !FABLE_COMPILER
219219
static member TryWith (computation: unit -> Task<_> , catchHandler: exn -> Task<_> , _: TryWith, True) = Task.tryWith computation catchHandler
220+
static member TryWith (computation: unit -> ValueTask<_> , catchHandler: exn -> ValueTask<_> , _: TryWith, True) = ValueTask.tryWith catchHandler computation
220221
#endif
221222
static member TryWith (computation: unit -> Lazy<_> , catchHandler: exn -> Lazy<_> , _: TryWith , _) = lazy (try (computation ()).Force () with e -> (catchHandler e).Force ()) : Lazy<_>
222223

@@ -245,7 +246,8 @@ type TryFinally =
245246
static member TryFinally ((computation: unit -> Id<_> , compensation: unit -> unit), _: TryFinally, _, _) = try computation () finally compensation ()
246247
static member TryFinally ((computation: unit -> Async<_>, compensation: unit -> unit), _: TryFinally, _, _) = async.TryFinally (computation (), compensation) : Async<_>
247248
#if !FABLE_COMPILER
248-
static member TryFinally ((computation: unit -> Task<_> , compensation: unit -> unit), _: TryFinally, _, True) = Task.tryFinally computation compensation : Task<_>
249+
static member TryFinally ((computation: unit -> Task<_> , compensation: unit -> unit), _: TryFinally, _, True) = Task.tryFinally computation compensation : Task<_>
250+
static member TryFinally ((computation: unit -> ValueTask<_>, compensation: unit -> unit), _: TryFinally, _, True) = ValueTask.tryFinally compensation computation : ValueTask<_>
249251
#endif
250252
static member TryFinally ((computation: unit -> Lazy<_> , compensation: unit -> unit), _: TryFinally, _, _) = lazy (try (computation ()).Force () finally compensation ()) : Lazy<_>
251253

@@ -281,7 +283,8 @@ type Using =
281283
static member Using (resource: 'T when 'T :> IDisposable, body: 'T -> 'R -> 'U , _: Using ) = (fun s -> try body resource s finally if not (isNull (box resource)) then resource.Dispose ()) : 'R->'U
282284
static member Using (resource: 'T when 'T :> IDisposable, body: 'T -> Async<'U>, _: Using ) = async.Using (resource, body)
283285
#if !FABLE_COMPILER
284-
static member Using (resource: 'T when 'T :> IDisposable, body: 'T -> Task<'U>, _: Using ) = Task.using resource body
286+
static member Using (resource: 'T when 'T :> IDisposable, body: 'T -> Task<'U> , _: Using) = Task.using resource body
287+
static member Using (resource: 'T when 'T :> IDisposable, body: 'T -> ValueTask<'U>, _: Using) = ValueTask.using resource body
285288
#endif
286289
static member Using (resource: 'T when 'T :> IDisposable, body: 'T -> Lazy<'U> , _: Using ) = lazy (try (body resource).Force () finally if not (isNull (box resource)) then resource.Dispose ()) : Lazy<'U>
287290

src/FSharpPlus/Extensions/ValueTask.fs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,58 @@ module ValueTask =
243243
else source.ConfigureAwait(false).GetAwaiter().UnsafeOnCompleted (fun () -> k source)
244244
tcs.Task |> ValueTask<unit>
245245

246+
/// Used to de-sugar try .. with .. blocks in Computation Expressions.
247+
let inline tryWith ([<InlineIfLambda>]compensation: exn -> ValueTask<'T>) ([<InlineIfLambda>]body: unit -> ValueTask<'T>) : ValueTask<'T> =
248+
let unwrapException (agg: AggregateException) =
249+
if agg.InnerExceptions.Count = 1 then agg.InnerExceptions.[0]
250+
else agg :> Exception
251+
try
252+
let task = body ()
253+
if task.IsCompleted then
254+
match task with
255+
| Succeeded _ -> task
256+
| Faulted exn -> compensation (unwrapException exn)
257+
| Canceled -> compensation (TaskCanceledException ())
258+
else
259+
let tcs = TaskCompletionSource<'T> ()
260+
let f = function
261+
| Succeeded r -> tcs.SetResult r
262+
| Faulted exn -> continueTask tcs (compensation (unwrapException exn)) (fun r -> try tcs.SetResult r with e -> tcs.SetException e)
263+
| Canceled -> continueTask tcs (compensation (TaskCanceledException ())) (fun r -> try tcs.SetResult r with e -> tcs.SetException e)
264+
task.ConfigureAwait(false).GetAwaiter().UnsafeOnCompleted (fun () -> f task)
265+
ValueTask<'T> tcs.Task
266+
with
267+
| :? AggregateException as exn -> compensation (unwrapException exn)
268+
| exn -> compensation exn
269+
270+
/// Used to de-sugar try .. finally .. blocks in Computation Expressions.
271+
let inline tryFinally ([<InlineIfLambda>]compensation : unit -> unit) ([<InlineIfLambda>]body: unit -> ValueTask<'T>) : ValueTask<'T> =
272+
let mutable ran = false
273+
let compensation () =
274+
if not ran then
275+
compensation ()
276+
ran <- true
277+
try
278+
let task = body ()
279+
if task.IsCompleted then compensation (); task
280+
else
281+
let tcs = TaskCompletionSource<'T> ()
282+
let f = function
283+
| Succeeded r -> tcs.SetResult r
284+
| Faulted exn -> tcs.SetException exn.InnerExceptions
285+
| Canceled -> tcs.SetCanceled ()
286+
task.ConfigureAwait(false).GetAwaiter().UnsafeOnCompleted (fun () -> compensation (); f task)
287+
ValueTask<'T> tcs.Task
288+
with _ ->
289+
compensation ()
290+
reraise ()
291+
292+
/// Used to de-sugar use .. blocks in Computation Expressions.
293+
let inline using (disp: 'T when 'T :> IDisposable) ([<InlineIfLambda>]body: 'T -> ValueTask<'U>) =
294+
tryFinally
295+
(fun () -> if not (isNull (box disp)) then disp.Dispose ())
296+
(fun () -> body disp)
297+
246298

247299
/// Raises an exception in the ValueTask
248300
let raise<'TResult> (``exception``: exn) = ValueTask<'TResult> (Task.FromException<'TResult> ``exception``)

0 commit comments

Comments
 (0)