11namespace ErrorHandling
22
3+ open System
4+ open System.Threading .Tasks
5+
36/// Functions for Result type (functor and monad).
47/// For applicatives, see Validation.
58[<RequireQualifiedAccess>] // RequireQualifiedAccess forces the `Result.xxx` prefix to be used
6- module Result =
9+ module internal Result =
710 let tee f = function
811 | Ok x -> f x; Ok x
912 | Error x -> Error x
@@ -81,7 +84,7 @@ module Result =
8184 | Ok option -> option
8285 | Error e -> failwithf " Error %A " e
8386
84- module Operators =
87+ module internal Operators =
8588 /// Result.bind
8689 let inline (>>= ) r f = Result.bind f r
8790
@@ -120,7 +123,7 @@ module Result =
120123 fR >> Result.mapError fE
121124
122125[<AutoOpen>]
123- module ResultComputationExpression =
126+ module internal ResultComputationExpression =
124127 // https://github.com/swlaschin/DomainModelingMadeFunctional/blob/master/src/OrderTaking/Result.fs#L178
125128
126129 type ResultBuilder () =
@@ -169,7 +172,7 @@ type Validation<'Success,'Failure> =
169172
170173/// Functions for the `Validation` type (mostly applicative)
171174[<RequireQualifiedAccess>] // RequireQualifiedAccess forces the `Validation.xxx` prefix to be used
172- module Validation =
175+ module internal Validation =
173176
174177 /// Apply a Validation<fn> to a Validation<x> applicatively
175178 let apply ( fV : Validation < _ , _ >) ( xV : Validation < _ , _ >) : Validation < _ , _ > =
@@ -210,7 +213,7 @@ module Validation =
210213//==============================================
211214
212215[<RequireQualifiedAccess>] // RequireQualifiedAccess forces the `Async.xxx` prefix to be used
213- module Async =
216+ module internal Async =
214217
215218 /// Lift a function to Async
216219 let map f xA =
@@ -245,64 +248,63 @@ module Async =
245248type AsyncResult < 'Success , 'Failure > =
246249 Async< Result< 'Success, 'Failure>>
247250
248- [<RequireQualifiedAccess>] // RequireQualifiedAccess forces the `AsyncResult.xxx` prefix to be used
249- module AsyncResult =
251+ [<RequireQualifiedAccess>]
252+ module internal AsyncResult =
250253
251254 /// Lift a function to AsyncResult
252- let map f ( x : AsyncResult < _ , _ >) : AsyncResult < _ , _ > =
255+ let map ( f : 'SuccessA -> 'SuccessB ) ( x : AsyncResult < 'SuccessA , 'Error >) : AsyncResult < 'SuccessB , 'Error > =
253256 Async.map ( Result.map f) x
254257
255258 /// Lift a function to AsyncResult
256- let mapError f ( x : AsyncResult < _ , _ >) : AsyncResult < _ , _ > =
259+ let mapError ( f : 'ErrorA -> 'ErrorB ) ( x : AsyncResult < 'Success , 'ErrorA >) : AsyncResult < 'Success , 'ErrorB > =
257260 Async.map ( Result.mapError f) x
258261
259262 /// Apply ignore to the internal value
260- let ignore x =
263+ let ignore ( x : AsyncResult < 'Success , 'Error >): AsyncResult < unit , 'Error > =
261264 x |> map ignore
262265
263266 /// Lift a value to AsyncResult
264- let retn x : AsyncResult < _ , _ > =
267+ let retn ( x : 'Success ) : AsyncResult < 'Success , 'Error > =
265268 x |> Result.Ok |> Async.retn
266269
267- /// Handles asynchronous exceptions and maps them into Failure cases using the provided function
268- let catch f ( x : AsyncResult < _ , _ >) : AsyncResult < _ , _ > =
270+ /// Handles asynchronous exceptions and maps them into Error cases using the provided function
271+ let catch ( f : exn -> 'Error ) ( x : AsyncResult < 'Success , 'Error >) : AsyncResult < 'Success , 'Error > =
269272 x
270273 |> Async.Catch
271274 |> Async.map( function
272275 | Choice1Of2 ( Ok v) -> Ok v
273276 | Choice1Of2 ( Error err) -> Error err
274277 | Choice2Of2 ex -> Error ( f ex))
275278
276-
277279 /// Apply an AsyncResult function to an AsyncResult value, monadically
278- let applyM ( fAsyncResult : AsyncResult < _ , _ >) ( xAsyncResult : AsyncResult < _ , _ >) : AsyncResult < _ , _ > =
280+ let applyM ( fAsyncResult : AsyncResult < 'SuccessA -> 'SuccessB , 'Error >) ( xAsyncResult : AsyncResult < 'SuccessA , 'Error >): AsyncResult < 'SuccessB , 'Error > =
279281 fAsyncResult |> Async.bind ( fun fResult ->
280- xAsyncResult |> Async.map ( fun xResult -> Result.apply fResult xResult))
282+ xAsyncResult |> Async.map ( fun xResult -> Result.apply fResult xResult)
283+ )
281284
282285 /// Apply an AsyncResult function to an AsyncResult value, applicatively
283- let applyA ( fAsyncResult : AsyncResult < _ , _ >) ( xAsyncResult : AsyncResult < _ , _ >) : AsyncResult < _ , _ > =
286+ let applyA ( fAsyncResult : AsyncResult < 'SuccessA -> 'SuccessB , 'Error list >) ( xAsyncResult : AsyncResult < 'SuccessA , 'Error list >): AsyncResult < 'SuccessB , 'Error list > =
284287 fAsyncResult |> Async.bind ( fun fResult ->
285- xAsyncResult |> Async.map ( fun xResult -> Validation.apply fResult xResult))
288+ xAsyncResult |> Async.map ( fun xResult -> Validation.apply fResult xResult)
289+ )
286290
287291 /// Apply a monadic function to an AsyncResult value
288- let bind ( f : 'a -> AsyncResult < 'b , 'c >) ( xAsyncResult : AsyncResult < _ , _ >) : AsyncResult < _ , _ > = async {
289- let! xResult = xAsyncResult
290- match xResult with
292+ let bind ( f : 'SuccessA -> AsyncResult < 'SuccessB , 'Error >) ( xAsyncResult : AsyncResult < 'SuccessA , 'Error >): AsyncResult < 'SuccessB , 'Error > = async {
293+ match ! xAsyncResult with
291294 | Ok x -> return ! f x
292295 | Error err -> return ( Error err)
293- }
296+ }
294297
295298 /// Apply a monadic function to an AsyncResult error
296- let bindError ( f : 'a -> AsyncResult < 'b , 'c >) ( xAsyncResult : AsyncResult < _ , _ >) : AsyncResult < _ , _ > = async {
297- let! xResult = xAsyncResult
298- match xResult with
299+ let bindError ( f : 'ErrorA -> AsyncResult < 'Success , 'ErrorB >) ( xAsyncResult : AsyncResult < 'Success , 'ErrorA >): AsyncResult < 'Success , 'ErrorB > = async {
300+ match ! xAsyncResult with
299301 | Ok x -> return ( Ok x)
300302 | Error err -> return ! f err
301- }
303+ }
302304
303305 /// Convert a list of AsyncResult into a AsyncResult<list> using monadic style.
304- /// Only the first error is returned. The error type need not be a list.
305- let sequenceM resultList =
306+ /// Only the first error is returned. The error type NEED NOT be a list.
307+ let sequenceM ( results : AsyncResult < 'Success , 'Error > list ): AsyncResult < 'Success list , 'Error > =
306308 let (<*>) = applyM
307309 let (<!>) = map
308310 let cons head tail = head:: tail
@@ -311,23 +313,21 @@ module AsyncResult =
311313
312314 // loop through the list, prepending each element
313315 // to the initial value
314- List.foldBack consR resultList initialValue
316+ List.foldBack consR results initialValue
315317
316- let tee f ( xAsyncResult : AsyncResult < _ , _ >): AsyncResult < _ , _ > =
317- async {
318- let! xResult = xAsyncResult
319- return xResult |> Result.tee f
320- }
318+ let tee ( f : 'Success -> unit ) ( xAsyncResult : AsyncResult < 'Success , 'Error >): AsyncResult < 'Success , 'Error > = async {
319+ let! xResult = xAsyncResult
320+ return xResult |> Result.tee f
321+ }
321322
322- let teeError f ( xAsyncResult : AsyncResult < _ , _ >): AsyncResult < _ , _ > =
323- async {
324- let! xResult = xAsyncResult
325- return xResult |> Result.teeError f
326- }
323+ let teeError ( f : 'Error -> unit ) ( xAsyncResult : AsyncResult < 'Success , 'Error >): AsyncResult < 'Success , 'Error > = async {
324+ let! xResult = xAsyncResult
325+ return xResult |> Result.teeError f
326+ }
327327
328328 /// Convert a list of AsyncResult into a AsyncResult<list> using applicative style.
329- /// All the errors are returned. The error type must be a list.
330- let sequenceA resultList =
329+ /// All the errors are returned. The error type MUST be a list.
330+ let sequenceA ( results : AsyncResult < 'Success , 'Error list > list ): AsyncResult < 'Success list , 'Error list > =
331331 let (<*>) = applyA
332332 let (<!>) = map
333333 let cons head tail = head:: tail
@@ -336,44 +336,48 @@ module AsyncResult =
336336
337337 // loop through the list, prepending each element
338338 // to the initial value
339- List.foldBack consR resultList initialValue
339+ List.foldBack consR results initialValue
340340
341341 //-----------------------------------
342342 // Converting between AsyncResults and other types
343343
344344 /// Lift a value into an Ok inside a AsyncResult
345- let ofSuccess x : AsyncResult < _ , _ > =
345+ let ofSuccess ( x : 'Success ) : AsyncResult < 'Success , 'Error > =
346346 x |> Result.Ok |> Async.retn
347347
348348 /// Lift a value into an Error inside a AsyncResult
349- let ofError x : AsyncResult < _ , _ > =
349+ let ofError ( x : 'Error ) : AsyncResult < 'Success , 'Error > =
350350 x |> Result.Error |> Async.retn
351351
352352 /// Lift a Result into an AsyncResult
353- let ofResult x : AsyncResult < _ , _ > =
353+ let ofResult ( x : Result < 'Success , 'Error >) : AsyncResult < 'Success , 'Error > =
354354 x |> Async.retn
355355
356- /// Lift an Option into an AsyncResult
357- let ofOption e x : AsyncResult < _ , _ > =
358- x |> Result.ofOption e |> ofResult
359-
360356 /// Lift a Async into an AsyncResult
361- let ofAsync x : AsyncResult < _ , _ > =
357+ let ofAsync ( x : Async < 'Success >) : AsyncResult < 'Success , 'Error > =
362358 x |> Async.map Result.Ok
363359
364360 /// Lift a Async into an AsyncResult and handles exception into Result
365- let ofAsyncCatch f x : AsyncResult < _ , _ > =
361+ let ofAsyncCatch ( f : exn -> 'Error ) ( x : Async < 'Success >) : AsyncResult < 'Success , 'Error > =
366362 x |> ofAsync |> catch f
367363
368364 /// Lift a Task into an AsyncResult
369- let ofTask x : AsyncResult < _ , _ > =
365+ let ofTask ( x : Task < 'Success >) : AsyncResult < 'Success , 'Error > =
370366 x |> Async.AwaitTask |> ofAsync
371367
372368 /// Lift a Task into an AsyncResult and handles exception into Result
373- let ofTaskCatch f x : AsyncResult < _ , _ > =
369+ let ofTaskCatch ( f : exn -> 'Error ) ( x : Task < 'Success >) : AsyncResult < 'Success , 'Error > =
374370 x |> ofTask |> catch f
375371
376- /// Run asyncResults in Parallel, handles the errors and concats results
372+ /// Lift a Task into an AsyncResult
373+ let ofEmptyTask ( x : Task ): AsyncResult < unit , 'Error > =
374+ x |> Async.AwaitTask |> ofAsync
375+
376+ /// Lift a Task into an AsyncResult and handles exception into Result
377+ let ofEmptyTaskCatch ( f : exn -> 'Error ) ( x : Task ): AsyncResult < unit , 'Error > =
378+ x |> ofEmptyTask |> catch f
379+
380+ /// Run asyncResults in Parallel, handles the errors and concats results
377381 let ofParallelAsyncResults < 'Success , 'Error > ( f : exn -> 'Error ) ( results : AsyncResult < 'Success , 'Error > list ): AsyncResult < 'Success list , 'Error list > =
378382 results
379383 |> List.map ( mapError List.singleton)
@@ -416,7 +420,7 @@ module AsyncResult =
416420 //-----------------------------------
417421 // Utilities lifted from Async
418422
419- let sleep ( ms : int ) =
423+ let sleep ( ms : int ): AsyncResult < unit , 'Error > =
420424 Async.Sleep ms |> ofAsync
421425
422426 module Operators =
@@ -438,15 +442,38 @@ module AsyncResult =
438442 /// AsyncResult.mapError
439443 let inline (<@>) r f = mapError f r
440444
445+ /// Kleisli composition (composition of 2 functions, which returns an AsyncResult)
446+ let inline (>= >) fR fR2 =
447+ fR >> bind fR2
448+
449+ /// Kleisli composition for errors (composition of 2 functions, which returns an AsyncResult)
450+ let inline (> -> ) fR fR2 =
451+ fR >> bindError fR2
452+
453+ /// Composition of 2 functions by mapping a Success from 1st function into the 2nd
454+ let inline (>!>) fR f =
455+ fR >> map f
456+
457+ /// Composition of 2 functions by mapping an Error from 1st function into the 2nd
458+ let inline (>@>) fR fE =
459+ fR >> mapError fE
460+
461+ /// Compose with tee function
462+ let inline (>@*>) fR f =
463+ fR >> tee f
464+
465+ /// Compose with tee error function
466+ let inline (>@@>) fR fE =
467+ fR >> teeError fE
468+
441469// ==================================
442470// AsyncResult computation expression
443471// ==================================
444472
445473/// The `asyncResult` computation expression is available globally without qualification
474+ /// See https://github.com/cmeeren/Cvdm.ErrorHandling/blob/master/src/Cvdm.ErrorHandling/AsyncResultBuilder.fs
446475[<AutoOpen>]
447- module AsyncResultComputationExpression =
448- open System
449-
476+ module internal AsyncResultComputationExpression =
450477 type AsyncResultBuilder () =
451478 member __.Return ( value : 'Success ): AsyncResult < 'Success , 'Error > =
452479 async.Return <| result.Return value
0 commit comments