Skip to content

Commit d019eca

Browse files
1eyewonderTheAngryByrd
authored andcommitted
Copied Validation functions for asyncValidation use
1 parent 31ded50 commit d019eca

File tree

7 files changed

+1219
-41
lines changed

7 files changed

+1219
-41
lines changed
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
namespace FsToolkit.ErrorHandling
2+
3+
/// AsyncValidation<'a, 'err> is defined as Async<Result<'a, 'err list>> meaning you can use many of the functions found in the Result and Async module.
4+
type AsyncValidation<'ok, 'error> = Async<Result<'ok, 'error list>>
5+
6+
[<RequireQualifiedAccess>]
7+
module AsyncValidation =
8+
let inline ok (value: 'ok) : AsyncValidation<'ok, 'error> =
9+
Ok value
10+
|> async.Return
11+
12+
let inline error (error: 'error) : AsyncValidation<'ok, 'error> =
13+
Error [ error ]
14+
|> async.Return
15+
16+
let inline ofResult (result: Result<'ok, 'error>) : AsyncValidation<'ok, 'error> =
17+
Result.mapError List.singleton result
18+
|> async.Return
19+
20+
let inline ofChoice (choice: Choice<'ok, 'error>) : AsyncValidation<'ok, 'error> =
21+
match choice with
22+
| Choice1Of2 x -> ok x
23+
| Choice2Of2 e -> error e
24+
25+
let inline apply
26+
(applier: AsyncValidation<'okInput -> 'okOutput, 'error>)
27+
(input: AsyncValidation<'okInput, 'error>)
28+
: AsyncValidation<'okOutput, 'error> =
29+
async {
30+
let! applier = applier
31+
let! input = input
32+
33+
return
34+
match applier, input with
35+
| Ok f, Ok x -> Ok(f x)
36+
| Error errs, Ok _
37+
| Ok _, Error errs -> Error errs
38+
| Error errs1, Error errs2 ->
39+
Error(
40+
errs1
41+
@ errs2
42+
)
43+
}
44+
45+
let inline retn (value: 'ok) : AsyncValidation<'ok, 'error> = ok value
46+
47+
let inline returnError (error: 'error) : AsyncValidation<'ok, 'error> =
48+
Error [ error ]
49+
|> async.Return
50+
51+
let inline orElse
52+
(ifError: AsyncValidation<'ok, 'errorOutput>)
53+
(result: AsyncValidation<'ok, 'errorInput>)
54+
: AsyncValidation<'ok, 'errorOutput> =
55+
async {
56+
let! result = result
57+
return!
58+
result
59+
|> Result.either ok (fun _ -> ifError)
60+
}
61+
62+
let inline orElseWith
63+
([<InlineIfLambda>] ifErrorFunc: 'errorInput list -> AsyncValidation<'ok, 'errorOutput>)
64+
(result: AsyncValidation<'ok, 'errorInput>)
65+
: AsyncValidation<'ok, 'errorOutput> =
66+
async {
67+
let! result = result
68+
return!
69+
match result with
70+
| Ok x -> ok x
71+
| Error err -> ifErrorFunc err
72+
}
73+
74+
let inline map
75+
([<InlineIfLambda>] mapper: 'okInput -> 'okOutput)
76+
(input: AsyncValidation<'okInput, 'error>)
77+
: AsyncValidation<'okOutput, 'error> =
78+
async {
79+
let! input = input
80+
return Result.map mapper input
81+
}
82+
83+
let inline map2
84+
([<InlineIfLambda>] mapper: 'okInput1 -> 'okInput2 -> 'okOutput)
85+
(input1: AsyncValidation<'okInput1, 'error>)
86+
(input2: AsyncValidation<'okInput2, 'error>)
87+
: AsyncValidation<'okOutput, 'error> =
88+
async {
89+
let! input1 = input1
90+
let! input2 = input2
91+
92+
return
93+
match input1, input2 with
94+
| Ok x, Ok y -> Ok(mapper x y)
95+
| Ok _, Error errs -> Error errs
96+
| Error errs, Ok _ -> Error errs
97+
| Error errs1, Error errs2 ->
98+
Error(
99+
errs1
100+
@ errs2
101+
)
102+
}
103+
104+
let inline map3
105+
([<InlineIfLambda>] mapper: 'okInput1 -> 'okInput2 -> 'okInput3 -> 'okOutput)
106+
(input1: AsyncValidation<'okInput1, 'error>)
107+
(input2: AsyncValidation<'okInput2, 'error>)
108+
(input3: AsyncValidation<'okInput3, 'error>)
109+
: AsyncValidation<'okOutput, 'error> =
110+
async {
111+
let! input1 = input1
112+
let! input2 = input2
113+
let! input3 = input3
114+
115+
return
116+
match input1, input2, input3 with
117+
| Ok x, Ok y, Ok z -> Ok(mapper x y z)
118+
| Error errs, Ok _, Ok _ -> Error errs
119+
| Ok _, Error errs, Ok _ -> Error errs
120+
| Ok _, Ok _, Error errs -> Error errs
121+
| Error errs1, Error errs2, Ok _ ->
122+
Error(
123+
errs1
124+
@ errs2
125+
)
126+
| Ok _, Error errs1, Error errs2 ->
127+
Error(
128+
errs1
129+
@ errs2
130+
)
131+
| Error errs1, Ok _, Error errs2 ->
132+
Error(
133+
errs1
134+
@ errs2
135+
)
136+
| Error errs1, Error errs2, Error errs3 ->
137+
Error(
138+
errs1
139+
@ errs2
140+
@ errs3
141+
)
142+
}
143+
144+
let inline mapError
145+
([<InlineIfLambda>] errorMapper: 'errorInput -> 'errorOutput)
146+
(input: AsyncValidation<'ok, 'errorInput>)
147+
: AsyncValidation<'ok, 'errorOutput> =
148+
async {
149+
let! input = input
150+
return Result.mapError (List.map errorMapper) input
151+
}
152+
153+
let inline mapErrors
154+
([<InlineIfLambda>] errorMapper: 'errorInput list -> 'errorOutput list)
155+
(input: AsyncValidation<'ok, 'errorInput>)
156+
: AsyncValidation<'ok, 'errorOutput> =
157+
async {
158+
let! input = input
159+
return Result.mapError errorMapper input
160+
}
161+
162+
let inline bind
163+
([<InlineIfLambda>] binder: 'okInput -> AsyncValidation<'okOutput, 'error>)
164+
(input: AsyncValidation<'okInput, 'error>)
165+
: AsyncValidation<'okOutput, 'error> =
166+
async {
167+
let! input = input
168+
169+
match input with
170+
| Ok x -> return! binder x
171+
| Error e -> return Error e
172+
}
173+
174+
let inline zip
175+
(left: AsyncValidation<'left, 'error>)
176+
(right: AsyncValidation<'right, 'error>)
177+
: AsyncValidation<'left * 'right, 'error> =
178+
async {
179+
let! left = left
180+
let! right = right
181+
182+
return
183+
match left, right with
184+
| Ok x1res, Ok x2res -> Ok(x1res, x2res)
185+
| Error e, Ok _ -> Error e
186+
| Ok _, Error e -> Error e
187+
| Error e1, Error e2 -> Error(e1 @ e2)
188+
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
namespace FsToolkit.ErrorHandling
2+
3+
open System
4+
5+
[<AutoOpen>]
6+
module AsyncValidationCE =
7+
8+
type AsyncValidationBuilder() =
9+
member inline _.Return(value: 'ok) : AsyncValidation<'ok, 'error> = AsyncValidation.ok value
10+
11+
member inline _.ReturnFrom(result: AsyncValidation<'ok, 'error>) : AsyncValidation<'ok, 'error> =
12+
result
13+
14+
member inline _.Bind
15+
(
16+
result: AsyncValidation<'okInput, 'error>,
17+
[<InlineIfLambda>] binder: 'okInput -> AsyncValidation<'okOutput, 'error>
18+
) : AsyncValidation<'okOutput, 'error> =
19+
AsyncValidation.bind binder result
20+
21+
member inline this.Zero() : AsyncValidation<unit, 'error> = this.Return()
22+
23+
member inline _.Delay
24+
([<InlineIfLambda>] generator: unit -> AsyncValidation<'ok, 'error>)
25+
: unit -> AsyncValidation<'ok, 'error> =
26+
generator
27+
28+
member inline _.Run
29+
([<InlineIfLambda>] generator: unit -> AsyncValidation<'ok, 'error>)
30+
: AsyncValidation<'ok, 'error> =
31+
generator ()
32+
33+
member inline this.Combine
34+
(
35+
result: AsyncValidation<unit, 'error>,
36+
[<InlineIfLambda>] binder: unit -> AsyncValidation<'ok, 'error>
37+
) : AsyncValidation<'ok, 'error> =
38+
this.Bind(result, binder)
39+
40+
member inline this.TryWith
41+
(
42+
[<InlineIfLambda>] generator: unit -> AsyncValidation<'ok, 'error>,
43+
[<InlineIfLambda>] handler: exn -> AsyncValidation<'ok, 'error>
44+
) : AsyncValidation<'ok, 'error> =
45+
try
46+
this.Run generator
47+
with e ->
48+
handler e
49+
50+
member inline this.TryFinally
51+
(
52+
[<InlineIfLambda>] generator: unit -> AsyncValidation<'ok, 'error>,
53+
[<InlineIfLambda>] compensation: unit -> unit
54+
) : AsyncValidation<'ok, 'error> =
55+
try
56+
this.Run generator
57+
finally
58+
compensation ()
59+
60+
member inline this.Using
61+
(
62+
resource: 'disposable :> IDisposable,
63+
[<InlineIfLambda>] binder: 'disposable -> AsyncValidation<'okOutput, 'error>
64+
) : AsyncValidation<'okOutput, 'error> =
65+
this.TryFinally(
66+
(fun () -> binder resource),
67+
(fun () ->
68+
if not (obj.ReferenceEquals(resource, null)) then
69+
resource.Dispose()
70+
)
71+
)
72+
73+
member inline this.While
74+
(
75+
[<InlineIfLambda>] guard: unit -> bool,
76+
[<InlineIfLambda>] generator: unit -> AsyncValidation<unit, 'error>
77+
) : AsyncValidation<unit, 'error> =
78+
let mutable doContinue = true
79+
let mutable result = Ok()
80+
81+
async {
82+
while doContinue
83+
&& guard () do
84+
let! x = generator ()
85+
match x with
86+
| Ok() -> ()
87+
| Error e ->
88+
doContinue <- false
89+
result <- Error e
90+
return result
91+
}
92+
93+
member inline this.For
94+
(
95+
sequence: #seq<'ok>,
96+
[<InlineIfLambda>] binder: 'ok -> AsyncValidation<unit, 'error>
97+
) : AsyncValidation<unit, 'error> =
98+
this.Using(
99+
sequence.GetEnumerator(),
100+
fun enum -> this.While(enum.MoveNext, this.Delay(fun () -> binder enum.Current))
101+
)
102+
103+
member inline _.BindReturn
104+
(
105+
input: AsyncValidation<'okInput, 'error>,
106+
[<InlineIfLambda>] mapper: 'okInput -> 'okOutput
107+
) : AsyncValidation<'okOutput, 'error> =
108+
AsyncValidation.map mapper input
109+
110+
member inline _.MergeSources
111+
(
112+
left: AsyncValidation<'left, 'error>,
113+
right: AsyncValidation<'right, 'error>
114+
) : AsyncValidation<'left * 'right, 'error> =
115+
AsyncValidation.zip left right
116+
117+
/// <summary>
118+
/// Method lets us transform data types into our internal representation. This is the identity method to recognize the self type.
119+
///
120+
/// See https://stackoverflow.com/questions/35286541/why-would-you-use-builder-source-in-a-custom-computation-expression-builder
121+
/// </summary>
122+
/// <param name="result"></param>
123+
/// <returns></returns>
124+
member inline _.Source(result: AsyncValidation<'ok, 'error>) : AsyncValidation<'ok, 'error> = result
125+
126+
let asyncValidation = AsyncValidationBuilder()
127+
128+
[<AutoOpen>]
129+
module AsyncValidationCEExtensions =
130+
131+
// Having members as extensions gives them lower priority in
132+
// overload resolution and allows skipping more type annotations.
133+
type AsyncValidationBuilder with
134+
135+
/// <summary>
136+
/// Needed to allow `for..in` and `for..do` functionality
137+
/// </summary>
138+
member inline _.Source(s: #seq<_>) : #seq<_> = s
139+
140+
/// <summary>
141+
/// Method lets us transform data types into our internal representation.
142+
/// </summary>
143+
member inline _.Source(s: Result<'ok, 'error>) : AsyncValidation<'ok, 'error> =
144+
AsyncValidation.ofResult s
145+
146+
/// <summary>
147+
/// Method lets us transform data types into our internal representation.
148+
/// </summary>
149+
/// <returns></returns>
150+
member inline _.Source(choice: Choice<'ok, 'error>) : AsyncValidation<'ok, 'error> =
151+
AsyncValidation.ofChoice choice
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
namespace FsToolkit.ErrorHandling.Operator.AsyncValidation
2+
3+
open FsToolkit.ErrorHandling
4+
5+
[<AutoOpen>]
6+
module AsyncValidation =
7+
let inline (<!>)
8+
([<InlineIfLambda>] mapper: 'okInput -> 'okOutput)
9+
(input: AsyncValidation<'okInput, 'error>)
10+
: AsyncValidation<'okOutput, 'error> =
11+
AsyncValidation.map mapper input
12+
13+
let inline (<!^>)
14+
([<InlineIfLambda>] mapper: 'okInput -> 'okOutput)
15+
(input: Result<'okInput, 'error>)
16+
: AsyncValidation<'okOutput, 'error> =
17+
AsyncValidation.map mapper (AsyncValidation.ofResult input)
18+
19+
let inline (<*>)
20+
(applier: AsyncValidation<('okInput -> 'okOutput), 'error>)
21+
(input: AsyncValidation<'okInput, 'error>)
22+
: AsyncValidation<'okOutput, 'error> =
23+
AsyncValidation.apply applier input
24+
25+
let inline (<*^>)
26+
(applier: AsyncValidation<('okInput -> 'okOutput), 'error>)
27+
(input: Result<'okInput, 'error>)
28+
: AsyncValidation<'okOutput, 'error> =
29+
AsyncValidation.apply applier (AsyncValidation.ofResult input)
30+
31+
let inline (>>=)
32+
(input: AsyncValidation<'okInput, 'error>)
33+
([<InlineIfLambda>] binder: 'okInput -> AsyncValidation<'okOutput, 'error>)
34+
: AsyncValidation<'okOutput, 'error> =
35+
AsyncValidation.bind binder input

src/FsToolkit.ErrorHandling/FsToolkit.ErrorHandling.fsproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
<Compile Include="Validation.fs" />
2222
<Compile Include="ValidationOp.fs" />
2323
<Compile Include="ValidationCE.fs" />
24+
<Compile Include="AsyncValidation.fs" />
25+
<Compile Include="AsyncValidationOp.fs" />
26+
<Compile Include="AsyncValidationCE.fs" />
2427
<Compile Include="Option.fs" />
2528
<Compile Include="OptionCE.fs" />
2629
<Compile Include="OptionOp.fs" />

0 commit comments

Comments
 (0)