Skip to content
Draft
Show file tree
Hide file tree
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
26 changes: 2 additions & 24 deletions src/FSharpPlus/Control/Comonad.fs
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,12 @@ type Extract =
#endif
#if !FABLE_COMPILER
static member Extract (f: Task<'T> ) = f.Result
#endif
#if !FABLE_COMPILER
static member Extract (f: ValueTask<'T> ) = f.Result
static member Extract (f: ValueTask<'T>) = f.Result
#endif
static member inline Invoke (x: '``Comonad<'T>``) : 'T =
let inline call_2 (_mthd: ^M, x: ^I) = ((^M or ^I) : (static member Extract : _ -> _) x)
call_2 (Unchecked.defaultof<Extract>, x)

#nowarn "0025" // (see nowarn comment below)

type Extend =
static member (=>>) (g: Async<'T> , f: Async<'T> -> 'U) = async.Return (f g) : Async<'U>
static member (=>>) (g: Lazy<'T> , f: Lazy<'T> -> 'U ) = Lazy<_>.Create (fun () -> f g) : Lazy<'U>
Expand Down Expand Up @@ -68,25 +64,7 @@ type Extend =
#endif

#if !FABLE_COMPILER
static member (=>>) (g: ValueTask<'T> , f: ValueTask<'T> -> 'U ) : ValueTask<'U> =
if g.IsCompletedSuccessfully then
try
let r = f g
ValueTask<'U> r
with e -> ValueTask<'U> (Task.FromException<'U> e)
else
let tcs = TaskCompletionSource<'U> ()
if g.IsCompleted then
match g with
| ValueTask.Faulted e -> tcs.SetException e
| ValueTask.Canceled -> tcs.SetCanceled ()
// nowarn here, this case has been handled already if g.IsCompleted
else
ValueTask.continueTask tcs g (fun _ ->
try tcs.SetResult (f g)
with e -> tcs.SetException e)
tcs.Task |> ValueTask<'U>

static member (=>>) (g: ValueTask<'T> , f: ValueTask<'T> -> 'U ) : ValueTask<'U> = ValueTask.extend f g
#endif

// Restricted Comonads
Expand Down
7 changes: 5 additions & 2 deletions src/FSharpPlus/Control/Monad.fs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ type TryWith =
static member TryWith (computation: unit -> Async<_> , catchHandler: exn -> Async<_> , _: TryWith , _) = async.TryWith ((computation ()), catchHandler)
#if !FABLE_COMPILER
static member TryWith (computation: unit -> Task<_> , catchHandler: exn -> Task<_> , _: TryWith, True) = Task.tryWith computation catchHandler
static member TryWith (computation: unit -> ValueTask<_> , catchHandler: exn -> ValueTask<_> , _: TryWith, True) = ValueTask.tryWith catchHandler computation
#endif
static member TryWith (computation: unit -> Lazy<_> , catchHandler: exn -> Lazy<_> , _: TryWith , _) = lazy (try (computation ()).Force () with e -> (catchHandler e).Force ()) : Lazy<_>

Expand Down Expand Up @@ -245,7 +246,8 @@ type TryFinally =
static member TryFinally ((computation: unit -> Id<_> , compensation: unit -> unit), _: TryFinally, _, _) = try computation () finally compensation ()
static member TryFinally ((computation: unit -> Async<_>, compensation: unit -> unit), _: TryFinally, _, _) = async.TryFinally (computation (), compensation) : Async<_>
#if !FABLE_COMPILER
static member TryFinally ((computation: unit -> Task<_> , compensation: unit -> unit), _: TryFinally, _, True) = Task.tryFinally computation compensation : Task<_>
static member TryFinally ((computation: unit -> Task<_> , compensation: unit -> unit), _: TryFinally, _, True) = Task.tryFinally computation compensation : Task<_>
static member TryFinally ((computation: unit -> ValueTask<_>, compensation: unit -> unit), _: TryFinally, _, True) = ValueTask.tryFinally compensation computation : ValueTask<_>
#endif
static member TryFinally ((computation: unit -> Lazy<_> , compensation: unit -> unit), _: TryFinally, _, _) = lazy (try (computation ()).Force () finally compensation ()) : Lazy<_>

Expand Down Expand Up @@ -281,7 +283,8 @@ type Using =
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
static member Using (resource: 'T when 'T :> IDisposable, body: 'T -> Async<'U>, _: Using ) = async.Using (resource, body)
#if !FABLE_COMPILER
static member Using (resource: 'T when 'T :> IDisposable, body: 'T -> Task<'U>, _: Using ) = Task.using resource body
static member Using (resource: 'T when 'T :> IDisposable, body: 'T -> Task<'U> , _: Using) = Task.using resource body
static member Using (resource: 'T when 'T :> IDisposable, body: 'T -> ValueTask<'U>, _: Using) = ValueTask.using resource body
#endif
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>

Expand Down
89 changes: 83 additions & 6 deletions src/FSharpPlus/Extensions/ValueTask.fs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
namespace FSharpPlus

#nowarn "0025" // (see nowarn comment below)
#if !FABLE_COMPILER

/// Additional operations on ValueTask<'T>
Expand All @@ -11,6 +12,7 @@ module ValueTask =
open System.Threading.Tasks
open FSharpPlus.Internals.Errors

/// Active pattern to match the state of a completed ValueTask
let inline (|Succeeded|Canceled|Faulted|) (t: ValueTask<'T>) =
if t.IsCompletedSuccessfully then Succeeded t.Result
elif t.IsCanceled then Canceled
Expand All @@ -28,6 +30,30 @@ module ValueTask =
if x.IsCompleted then f x
else x.ConfigureAwait(false).GetAwaiter().UnsafeOnCompleted (fun () -> f x)


let inline internal extendNotSuccessfullyCompletedTask (f: ValueTask<'T> -> 'U) (g: ValueTask<'T>) : ValueTask<'U> =
let tcs = TaskCompletionSource<'U> ()
if g.IsCompleted then
match g with
| Faulted e -> tcs.SetException e
| Canceled -> tcs.SetCanceled ()
// nowarn here, this case has been assumed as not completed successfully
else
continueTask tcs g (fun _ ->
try tcs.SetResult (f g)
with e -> tcs.SetException e)
tcs.Task |> ValueTask<'U>

let inline internal extend (f: ValueTask<'T> -> 'U) (g: ValueTask<'T>) : ValueTask<'U> =
if g.IsCompletedSuccessfully then
try
let r = f g
ValueTask<'U> r
with e -> ValueTask<'U> (Task.FromException<'U> e)
else
extendNotSuccessfullyCompletedTask f g


/// Creates a ValueTask from a value
let result (value: 'T) : ValueTask<'T> =
#if NET5_0_OR_GREATER
Expand Down Expand Up @@ -226,15 +252,66 @@ module ValueTask =

/// <summary>Creates a ValueTask that ignores the result of the source ValueTask.</summary>
/// <remarks>It can be used to convert non-generic ValueTask to unit ValueTask.</remarks>
let ignore (source: ValueTask<'T>) =
if source.IsCompletedSuccessfully then
source.GetAwaiter().GetResult() |> ignore
Unchecked.defaultof<_>
let ignore (source: ValueTask) : ValueTask<unit> =
if source.IsCompletedSuccessfully then Unchecked.defaultof<_>
else
new ValueTask (source.AsTask ())
let tcs = TaskCompletionSource<unit> ()
if source.IsFaulted then tcs.SetException (Unchecked.nonNull (source.AsTask().Exception)).InnerExceptions
elif source.IsCanceled then tcs.SetCanceled ()
else
let k (t: ValueTask) : unit =
if t.IsCanceled then tcs.SetCanceled ()
elif t.IsFaulted then tcs.SetException (Unchecked.nonNull (source.AsTask().Exception)).InnerExceptions
else tcs.SetResult ()
if source.IsCompleted then k source
else source.ConfigureAwait(false).GetAwaiter().UnsafeOnCompleted (fun () -> k source)
tcs.Task |> ValueTask<unit>

/// Used to de-sugar try .. with .. blocks in Computation Expressions.
let rec tryWith (compensation: exn -> ValueTask<'T>) (body: unit -> ValueTask<'T>) : ValueTask<'T> =
let unwrapException (agg: AggregateException) =
if agg.InnerExceptions.Count = 1 then agg.InnerExceptions.[0]
else agg :> Exception
try
let task = body ()
let f = function
| Succeeded _ | Canceled -> task
| Faulted e -> extendNotSuccessfullyCompletedTask (fun (_: ValueTask<'T>) -> compensation (unwrapException e)) task |> join

if task.IsCompleted then f task
else extend (fun (x: ValueTask<'T>) -> tryWith compensation (fun () -> x)) task |> join
with
| :? AggregateException as exn -> compensation (unwrapException exn)
| exn -> compensation exn

/// Used to de-sugar try .. finally .. blocks in Computation Expressions.
let tryFinally (compensation : unit -> unit) (body: unit -> ValueTask<'T>) : ValueTask<'T> =
let mutable ran = false
let compensation () =
if not ran then
compensation ()
ran <- true
try
let task = body ()
let rec loop (task: ValueTask<'T>) (compensation : unit -> unit) =
let f = function
| Succeeded _ -> compensation (); task
| Faulted _ -> extend (fun (x: ValueTask<'T>) -> compensation (); x) task |> join
| Canceled -> task
if task.IsCompleted then f task
else extend (fun (x: ValueTask<'T>) -> (loop x compensation: ValueTask<_>)) task |> join
loop task compensation
with _ ->
compensation ()
reraise ()

/// Used to de-sugar use .. blocks in Computation Expressions.
let using (disp: 'T when 'T :> IDisposable) (body: 'T -> ValueTask<'U>) =
tryFinally
(fun () -> if not (isNull (box disp)) then disp.Dispose ())
(fun () -> body disp)

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

#endif
Loading
Loading