@@ -2,6 +2,8 @@ namespace FSharp.Data.GraphQL
2
2
3
3
open System
4
4
open System.Collections .Generic
5
+ open System.Linq
6
+ open System.Threading .Tasks
5
7
6
8
#nowarn " 25"
7
9
@@ -15,185 +17,210 @@ type AsyncVal<'T> =
15
17
| Async of asynchronous : Async<'T>
16
18
| Failure of exn : Exception
17
19
18
- static member Zero = Value( Unchecked.defaultof< 'T>)
20
+ static member Zero = Value ( Unchecked.defaultof< 'T>)
19
21
override x.ToString () =
20
22
match x with
21
- | Value v -> " AsyncVal(" + v.ToString() + " )"
23
+ | Value v -> " AsyncVal(" + v.ToString () + " )"
22
24
| Async _ -> " AsyncVal(Async<>)"
23
25
| Failure f -> " AsyncVal(Failure:" + f.Message + " )"
24
26
25
27
[<RequireQualifiedAccess>]
26
28
module AsyncVal =
27
29
28
30
/// 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
30
32
31
33
/// 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
33
35
34
36
/// 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
36
38
37
39
/// Returns value wrapped by current AsyncVal. If it's part of Async computation,
38
40
/// it's executed synchronously and then value is returned.
39
41
/// 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 >) =
41
43
match x with
42
44
| Value v -> v
43
45
| Async a -> a |> Async.RunSynchronously
44
- | Failure f -> f.Reraise()
46
+ | Failure f -> f.Reraise ()
45
47
46
48
/// Create new AsyncVal from Async computation.
47
- let inline ofAsync ( a : Async < 'T >) = Async( a)
49
+ let inline ofAsync ( a : Async < 'T >) = Async ( a)
48
50
49
51
/// Returns an AsyncVal wrapper around provided Async computation.
50
- let inline wrap ( v : 'T ) = Value( v)
52
+ let inline wrap ( v : 'T ) = Value ( v)
51
53
52
54
/// Converts AsyncVal to Async computation.
53
- let toAsync ( x : AsyncVal < 'T >) =
55
+ let toAsync ( x : AsyncVal < 'T >) =
54
56
match x with
55
57
| Value v -> async.Return v
56
58
| 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)
58
67
59
68
/// Returns an empty AsyncVal with immediatelly executed value.
60
69
let inline empty < 'T > : AsyncVal < 'T > = AsyncVal< 'T>. Zero
61
70
62
71
/// Maps content of AsyncVal using provided mapping function, returning new
63
72
/// AsyncVal as the result.
64
- let map ( fn : 'T -> 'U ) ( x : AsyncVal < 'T >) =
73
+ let map ( fn : 'T -> 'U ) ( x : AsyncVal < 'T >) =
65
74
match x with
66
- | Value v -> Value( fn v)
75
+ | Value v -> Value ( fn v)
67
76
| Async a ->
68
- Async( async {
77
+ Async ( async {
69
78
let! result = a
70
79
return fn result
71
80
})
72
- | Failure f -> Failure( f)
81
+ | Failure f -> Failure ( f)
73
82
74
83
75
84
/// 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 >) =
77
86
match x with
78
- | Value v -> Value( Ok v)
87
+ | Value v -> Value ( Ok v)
79
88
| Async a ->
80
- Async( async {
89
+ Async ( async {
81
90
try
82
91
let! v = a
83
92
return Ok v
84
- with e -> return fn path e |> Error
93
+ with e ->
94
+ return fn path e |> Error
85
95
})
86
- | Failure f -> Value( fn path f |> Error)
96
+ | Failure f -> Value ( fn path f |> Error)
87
97
|> map ( Result.mapError ( List.map ( GQLProblemDetails.OfFieldExecutionError ( path |> List.rev))))
88
98
89
99
90
100
/// Folds content of AsyncVal over provided initial state zero using provided fn.
91
101
/// 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 > =
93
103
match x with
94
- | Value v -> Value( fn zero v)
104
+ | Value v -> Value ( fn zero v)
95
105
| Async a ->
96
- Async( async {
106
+ Async ( async {
97
107
let! res = a
98
108
return fn zero res
99
109
})
100
- | Failure f -> Failure( f)
110
+ | Failure f -> Failure ( f)
101
111
102
112
103
113
/// 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 > =
105
115
match x with
106
116
| Value v -> binder v
107
117
| Async a ->
108
- Async( async {
118
+ Async ( async {
109
119
let! value = a
110
120
let bound = binder value
111
121
match bound with
112
122
| Value v -> return v
113
123
| Async a -> return ! a
114
- | Failure f -> return f.Reraise()
124
+ | Failure f -> return f.Reraise ()
115
125
})
116
- | Failure f -> Failure( f)
126
+ | Failure f -> Failure ( f)
117
127
118
128
/// Converts array of AsyncVals into AsyncVal with array results.
119
129
/// In case when are non-immediate values in provided array, they are
120
130
/// executed asynchronously, one by one with regard to their order in array.
121
131
/// Returned array maintain order of values.
122
132
/// 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 []> =
124
134
if values.Length = 0 then Value [||]
125
135
elif values |> Array.exists isAsync then
126
- Async( async {
136
+ Async ( async {
127
137
let results = Array.zeroCreate values.Length
138
+ let exceptions = ResizeArray values.Length
128
139
for i = 0 to values.Length - 1 do
129
140
let v = values.[ i]
130
141
match v with
131
142
| Value v -> results.[ i] <- v
132
143
| Async a ->
133
144
let! r = a
134
145
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)
139
162
140
163
/// Converts array of AsyncVals into AsyncVal with array results.
141
164
/// In case when are non-immediate values in provided array, they are
142
165
/// executed all in parallel, in unordered fashion. Order of values
143
166
/// inside returned array is maintained.
144
167
/// 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 []> =
146
169
if values.Length = 0 then Value [||]
147
170
else
148
- let indexes = List<_>( 0 )
149
- let continuations = List<_>( 0 )
171
+ let indexes = List<_> ( 0 )
172
+ let continuations = List<_> ( 0 )
150
173
let results = Array.zeroCreate values.Length
174
+ let exceptions = ResizeArray values.Length
151
175
for i = 0 to values.Length - 1 do
152
176
let value = values.[ i]
153
177
match value with
154
178
| Value v -> results.[ i] <- v
155
179
| Async a ->
156
180
indexes.Add i
157
181
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
+ })
167
194
168
195
/// Converts array of AsyncVals of arrays into AsyncVal with array results
169
196
/// by calling collectParallel and then appending the results.
170
- let appendParallel ( values : AsyncVal < 'T []> []) : AsyncVal < 'T []> =
197
+ let appendParallel ( values : AsyncVal < 'T []>[]) : AsyncVal < 'T []> =
171
198
values
172
199
|> collectParallel
173
200
|> map ( Array.fold Array.append Array.empty)
174
201
175
202
/// Converts array of AsyncVals of arrays into AsyncVal with array results
176
203
/// by calling collectSequential and then appending the results.
177
- let appendSequential ( values : AsyncVal < 'T []> []) : AsyncVal < 'T []> =
204
+ let appendSequential ( values : AsyncVal < 'T []>[]) : AsyncVal < 'T []> =
178
205
values
179
206
|> collectSequential
180
207
|> map ( Array.fold Array.append Array.empty)
181
208
182
209
type AsyncValBuilder () =
183
210
member _.Zero () = AsyncVal.empty
184
211
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 {
191
217
let! value = a
192
218
let bound = binder value
193
219
match bound with
194
220
| Value v -> return v
195
221
| Async a -> return ! a
196
- | Failure f -> return f.Reraise() })
222
+ | Failure f -> return f.Reraise ()
223
+ })
197
224
198
225
199
226
[<AutoOpen>]
@@ -203,21 +230,21 @@ module AsyncExtensions =
203
230
let asyncVal = AsyncValBuilder ()
204
231
205
232
/// 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
207
234
208
235
/// 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
210
237
211
238
type Microsoft.FSharp.Control.AsyncBuilder with
212
239
213
- member _.ReturnFrom ( v : AsyncVal < 'T >) =
240
+ member _.ReturnFrom ( v : AsyncVal < 'T >) =
214
241
match v with
215
242
| Value v -> async.Return v
216
243
| Async a -> async.ReturnFrom a
217
244
| Failure f -> async.Return ( raise f)
218
245
219
- member _.Bind ( v : AsyncVal < 'T >, binder ) =
246
+ member _.Bind ( v : AsyncVal < 'T >, binder ) =
220
247
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