@@ -2,6 +2,8 @@ namespace FSharp.Data.GraphQL
22
33open System
44open System.Collections .Generic
5+ open System.Linq
6+ open System.Threading .Tasks
57
68#nowarn " 25"
79
@@ -15,185 +17,210 @@ type AsyncVal<'T> =
1517 | Async of asynchronous : Async<'T>
1618 | Failure of exn : Exception
1719
18- static member Zero = Value( Unchecked.defaultof< 'T>)
20+ static member Zero = Value ( Unchecked.defaultof< 'T>)
1921 override x.ToString () =
2022 match x with
21- | Value v -> " AsyncVal(" + v.ToString() + " )"
23+ | Value v -> " AsyncVal(" + v.ToString () + " )"
2224 | Async _ -> " AsyncVal(Async<>)"
2325 | Failure f -> " AsyncVal(Failure:" + f.Message + " )"
2426
2527[<RequireQualifiedAccess>]
2628module AsyncVal =
2729
2830 /// Returns true if AsyncVal wraps an Async computation, otherwise false.
29- let inline isAsync ( x : AsyncVal < 'T >) = match x with | Async _ -> true | _ -> false
31+ let inline isAsync ( x : AsyncVal < 'T >) = match x with | Async _ -> true | _ -> false
3032
3133 /// Returns true if AsyncVal contains immediate result, otherwise false.
32- let inline isSync ( x : AsyncVal < 'T >) = match x with | Value _ -> true | _ -> false
34+ let inline isSync ( x : AsyncVal < 'T >) = match x with | Value _ -> true | _ -> false
3335
3436 /// Returns true if the AsyncVal failed, otherwise false
35- let inline isFailure ( x : AsyncVal < 'T >) = match x with | Failure _ -> true | _ -> false
37+ let inline isFailure ( x : AsyncVal < 'T >) = match x with | Failure _ -> true | _ -> false
3638
3739 /// Returns value wrapped by current AsyncVal. If it's part of Async computation,
3840 /// it's executed synchronously and then value is returned.
3941 /// If the asyncVal failed, then the exception that caused the failure is raised
40- let get ( x : AsyncVal < 'T >) =
42+ let get ( x : AsyncVal < 'T >) =
4143 match x with
4244 | Value v -> v
4345 | Async a -> a |> Async.RunSynchronously
44- | Failure f -> f.Reraise()
46+ | Failure f -> f.Reraise ()
4547
4648 /// Create new AsyncVal from Async computation.
47- let inline ofAsync ( a : Async < 'T >) = Async( a)
49+ let inline ofAsync ( a : Async < 'T >) = Async ( a)
4850
4951 /// Returns an AsyncVal wrapper around provided Async computation.
50- let inline wrap ( v : 'T ) = Value( v)
52+ let inline wrap ( v : 'T ) = Value ( v)
5153
5254 /// Converts AsyncVal to Async computation.
53- let toAsync ( x : AsyncVal < 'T >) =
55+ let toAsync ( x : AsyncVal < 'T >) =
5456 match x with
5557 | Value v -> async.Return v
5658 | Async a -> a
57- | Failure f -> async.Return ( f.Reraise())
59+ | Failure f -> async.Return ( f.Reraise ())
60+
61+ /// Converts AsyncVal to Async computation.
62+ let toTask ( x : AsyncVal < 'T >) =
63+ match x with
64+ | Value v -> Task.FromResult ( v)
65+ | Async a -> Async.StartAsTask ( a)
66+ | Failure f -> Task.FromException< 'T> ( f)
5867
5968 /// Returns an empty AsyncVal with immediatelly executed value.
6069 let inline empty < 'T > : AsyncVal < 'T > = AsyncVal< 'T>. Zero
6170
6271 /// Maps content of AsyncVal using provided mapping function, returning new
6372 /// AsyncVal as the result.
64- let map ( fn : 'T -> 'U ) ( x : AsyncVal < 'T >) =
73+ let map ( fn : 'T -> 'U ) ( x : AsyncVal < 'T >) =
6574 match x with
66- | Value v -> Value( fn v)
75+ | Value v -> Value ( fn v)
6776 | Async a ->
68- Async( async {
77+ Async ( async {
6978 let! result = a
7079 return fn result
7180 })
72- | Failure f -> Failure( f)
81+ | Failure f -> Failure ( f)
7382
7483
7584 /// Applies rescue fn in case when contained Async value throws an exception.
76- let rescue path ( fn : FieldPath -> exn -> IGQLError list ) ( x : AsyncVal < 't >) =
85+ let rescue path ( fn : FieldPath -> exn -> IGQLError list ) ( x : AsyncVal < 't >) =
7786 match x with
78- | Value v -> Value( Ok v)
87+ | Value v -> Value ( Ok v)
7988 | Async a ->
80- Async( async {
89+ Async ( async {
8190 try
8291 let! v = a
8392 return Ok v
84- with e -> return fn path e |> Error
93+ with e ->
94+ return fn path e |> Error
8595 })
86- | Failure f -> Value( fn path f |> Error)
96+ | Failure f -> Value ( fn path f |> Error)
8797 |> map ( Result.mapError ( List.map ( GQLProblemDetails.OfFieldExecutionError ( path |> List.rev))))
8898
8999
90100 /// Folds content of AsyncVal over provided initial state zero using provided fn.
91101 /// Returns new AsyncVal as a result.
92- let fold ( fn : 'State -> 'T -> 'State ) ( zero : 'State ) ( x : AsyncVal < 'T >) : AsyncVal < 'State > =
102+ let fold ( fn : 'State -> 'T -> 'State ) ( zero : 'State ) ( x : AsyncVal < 'T >) : AsyncVal < 'State > =
93103 match x with
94- | Value v -> Value( fn zero v)
104+ | Value v -> Value ( fn zero v)
95105 | Async a ->
96- Async( async {
106+ Async ( async {
97107 let! res = a
98108 return fn zero res
99109 })
100- | Failure f -> Failure( f)
110+ | Failure f -> Failure ( f)
101111
102112
103113 /// Binds AsyncVal using binder function to produce new AsyncVal.
104- let bind ( binder : 'T -> AsyncVal < 'U >) ( x : AsyncVal < 'T >) : AsyncVal < 'U > =
114+ let bind ( binder : 'T -> AsyncVal < 'U >) ( x : AsyncVal < 'T >) : AsyncVal < 'U > =
105115 match x with
106116 | Value v -> binder v
107117 | Async a ->
108- Async( async {
118+ Async ( async {
109119 let! value = a
110120 let bound = binder value
111121 match bound with
112122 | Value v -> return v
113123 | Async a -> return ! a
114- | Failure f -> return f.Reraise()
124+ | Failure f -> return f.Reraise ()
115125 })
116- | Failure f -> Failure( f)
126+ | Failure f -> Failure ( f)
117127
118128 /// Converts array of AsyncVals into AsyncVal with array results.
119129 /// In case when are non-immediate values in provided array, they are
120130 /// executed asynchronously, one by one with regard to their order in array.
121131 /// Returned array maintain order of values.
122132 /// If the array contains a Failure, then the entire array will not resolve
123- let collectSequential ( values : AsyncVal < 'T > []) : AsyncVal < 'T []> =
133+ let collectSequential ( values : AsyncVal < 'T >[]) : AsyncVal < 'T []> =
124134 if values.Length = 0 then Value [||]
125135 elif values |> Array.exists isAsync then
126- Async( async {
136+ Async ( async {
127137 let results = Array.zeroCreate values.Length
138+ let exceptions = ResizeArray values.Length
128139 for i = 0 to values.Length - 1 do
129140 let v = values.[ i]
130141 match v with
131142 | Value v -> results.[ i] <- v
132143 | Async a ->
133144 let! r = a
134145 results.[ i] <- r
135- | Failure f ->
136- results.[ i] <- f.Reraise()
137- return results })
138- else Value ( values |> Array.map ( fun ( Value v ) -> v))
146+ | Failure f -> exceptions.Add f
147+ match exceptions.Count with
148+ | 0 -> return results
149+ | 1 -> return exceptions.First() .Reraise ()
150+ | _ -> return AggregateException exceptions |> raise
151+ })
152+ else
153+ let exceptions =
154+ values
155+ |> Array.choose ( function
156+ | Failure f -> Some f
157+ | _ -> None)
158+ match exceptions.Length with
159+ | 0 -> Value ( values |> Array.map ( fun ( Value v ) -> v))
160+ | 1 -> Failure ( exceptions.First ())
161+ | _ -> Failure ( AggregateException exceptions)
139162
140163 /// Converts array of AsyncVals into AsyncVal with array results.
141164 /// In case when are non-immediate values in provided array, they are
142165 /// executed all in parallel, in unordered fashion. Order of values
143166 /// inside returned array is maintained.
144167 /// If the array contains a Failure, then the entire array will not resolve
145- let collectParallel ( values : AsyncVal < 'T > []) : AsyncVal < 'T []> =
168+ let collectParallel ( values : AsyncVal < 'T >[]) : AsyncVal < 'T []> =
146169 if values.Length = 0 then Value [||]
147170 else
148- let indexes = List<_>( 0 )
149- let continuations = List<_>( 0 )
171+ let indexes = List<_> ( 0 )
172+ let continuations = List<_> ( 0 )
150173 let results = Array.zeroCreate values.Length
174+ let exceptions = ResizeArray values.Length
151175 for i = 0 to values.Length - 1 do
152176 let value = values.[ i]
153177 match value with
154178 | Value v -> results.[ i] <- v
155179 | Async a ->
156180 indexes.Add i
157181 continuations.Add a
158- | Failure f ->
159- results.[ i] <- f.Reraise()
160- if indexes.Count = 0
161- then Value( results)
162- else Async( async {
163- let! vals = continuations |> Async.Parallel
164- for i = 0 to indexes.Count - 1 do
165- results.[ indexes.[ i]] <- vals.[ i]
166- return results })
182+ | Failure f -> exceptions.Add f
183+ match exceptions.Count with
184+ | 1 -> AsyncVal.Failure ( exceptions.First ())
185+ | count when count > 1 -> AsyncVal.Failure ( AggregateException exceptions)
186+ | _ ->
187+ if indexes.Count = 0 then Value ( results)
188+ else Async ( async {
189+ let! vals = continuations |> Async.Parallel
190+ for i = 0 to indexes.Count - 1 do
191+ results.[ indexes.[ i]] <- vals.[ i]
192+ return results
193+ })
167194
168195 /// Converts array of AsyncVals of arrays into AsyncVal with array results
169196 /// by calling collectParallel and then appending the results.
170- let appendParallel ( values : AsyncVal < 'T []> []) : AsyncVal < 'T []> =
197+ let appendParallel ( values : AsyncVal < 'T []>[]) : AsyncVal < 'T []> =
171198 values
172199 |> collectParallel
173200 |> map ( Array.fold Array.append Array.empty)
174201
175202 /// Converts array of AsyncVals of arrays into AsyncVal with array results
176203 /// by calling collectSequential and then appending the results.
177- let appendSequential ( values : AsyncVal < 'T []> []) : AsyncVal < 'T []> =
204+ let appendSequential ( values : AsyncVal < 'T []>[]) : AsyncVal < 'T []> =
178205 values
179206 |> collectSequential
180207 |> map ( Array.fold Array.append Array.empty)
181208
182209type AsyncValBuilder () =
183210 member _.Zero () = AsyncVal.empty
184211 member _.Return v = AsyncVal.wrap v
185- member _.ReturnFrom ( v : AsyncVal < _ >) = v
186- member _.ReturnFrom ( a : Async < _ >) = AsyncVal.ofAsync a
187- member _.Bind ( v : AsyncVal < 'T >, binder : 'T -> AsyncVal < 'U >) =
188- AsyncVal.bind binder v
189- member _.Bind ( a : Async < 'T >, binder : 'T -> AsyncVal < 'U >) =
190- Async( async {
212+ member _.ReturnFrom ( v : AsyncVal < _ >) = v
213+ member _.ReturnFrom ( a : Async < _ >) = AsyncVal.ofAsync a
214+ member _.Bind ( v : AsyncVal < 'T >, binder : 'T -> AsyncVal < 'U >) = AsyncVal.bind binder v
215+ member _.Bind ( a : Async < 'T >, binder : 'T -> AsyncVal < 'U >) =
216+ Async ( async {
191217 let! value = a
192218 let bound = binder value
193219 match bound with
194220 | Value v -> return v
195221 | Async a -> return ! a
196- | Failure f -> return f.Reraise() })
222+ | Failure f -> return f.Reraise ()
223+ })
197224
198225
199226[<AutoOpen>]
@@ -203,21 +230,21 @@ module AsyncExtensions =
203230 let asyncVal = AsyncValBuilder ()
204231
205232 /// Active pattern used for checking if AsyncVal contains immediate value.
206- let (| Immediate | _ |) ( x : AsyncVal < 'T >) = match x with | Value v -> Some v | _ -> None
233+ let (| Immediate | _ |) ( x : AsyncVal < 'T >) = match x with | Value v -> Some v | _ -> None
207234
208235 /// Active patter used for checking if AsyncVal wraps an Async computation.
209- let (| Async | _ |) ( x : AsyncVal < 'T >) = match x with | Async a -> Some a | _ -> None
236+ let (| Async | _ |) ( x : AsyncVal < 'T >) = match x with | Async a -> Some a | _ -> None
210237
211238 type Microsoft.FSharp.Control.AsyncBuilder with
212239
213- member _.ReturnFrom ( v : AsyncVal < 'T >) =
240+ member _.ReturnFrom ( v : AsyncVal < 'T >) =
214241 match v with
215242 | Value v -> async.Return v
216243 | Async a -> async.ReturnFrom a
217244 | Failure f -> async.Return ( raise f)
218245
219- member _.Bind ( v : AsyncVal < 'T >, binder ) =
246+ member _.Bind ( v : AsyncVal < 'T >, binder ) =
220247 match v with
221- | Value v -> async.Bind( async.Return v, binder)
222- | Async a -> async.Bind( a, binder)
223- | Failure f -> async.Bind( async.Return ( raise f), binder)
248+ | Value v -> async.Bind ( async.Return v, binder)
249+ | Async a -> async.Bind ( a, binder)
250+ | Failure f -> async.Bind ( async.Return ( raise f), binder)
0 commit comments