Skip to content

Commit 5e6af8a

Browse files
authored
Merge pull request #8 from TypedUseCase/feature/update-error-handling
Update error handling
2 parents 6aadcc2 + c367dc0 commit 5e6af8a

File tree

2 files changed

+86
-58
lines changed

2 files changed

+86
-58
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
<!-- There is always Unreleased section on the top. Subsections (Add, Changed, Fix, Removed) should be Add as needed. -->
44
## Unreleased
5+
- [_Internal_] Update `ErrorHandling`
56

67
## 3.0.0 - 2021-08-12
78
- [**BC**] Change function `Parser.parse` to return Async Result

src/ErrorHandling/Result.fs

Lines changed: 85 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
namespace 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 =
245248
type 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

Comments
 (0)