Skip to content

Commit 8ec37d9

Browse files
1eyewonderTheAngryByrd
authored andcommitted
Added and updated ResultOption functions w/ related tests
1 parent c51f654 commit 8ec37d9

File tree

4 files changed

+751
-19
lines changed

4 files changed

+751
-19
lines changed
Lines changed: 91 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,107 @@
11
namespace FsToolkit.ErrorHandling
22

3-
43
[<RequireQualifiedAccess>]
54
module ResultOption =
65

7-
let map f ro = Result.map (Option.map f) ro
6+
let inline retn x = Ok(Some x)
7+
8+
let inline map
9+
([<InlineIfLambda>] mapper: 'okInput -> 'okOutput)
10+
(input: Result<'okInput option, 'error>)
11+
: Result<'okOutput option, 'error> =
12+
Result.map (Option.map mapper) input
813

9-
let bind f ro =
14+
let inline mapError
15+
([<InlineIfLambda>] mapper: 'errorInput -> 'errorOutput)
16+
(input: Result<'ok option, 'errorInput>)
17+
: Result<'ok option, 'errorOutput> =
18+
Result.mapError mapper input
19+
20+
let inline bind
21+
([<InlineIfLambda>] binder: 'okInput -> Result<'okOutput option, 'error>)
22+
(input: Result<'okInput option, 'error>)
23+
: Result<'okOutput option, 'error> =
1024
Result.bind
1125
(function
12-
| Some x -> f x
26+
| Some x -> binder x
1327
| None -> Ok None)
14-
ro
28+
input
29+
30+
let inline apply
31+
(applier: Result<('okInput -> 'okOutput) option, 'error>)
32+
(input: Result<'okInput option, 'error>)
33+
: Result<'okOutput option, 'error> =
34+
match (applier, input) with
35+
| Ok f, Ok x ->
36+
match f, x with
37+
| Some f', Some x' -> Ok(Some(f' x'))
38+
| _ -> Ok None
39+
| Error e, _
40+
| _, Error e -> Error e
1541

16-
let retn x = Ok(Some x)
1742

18-
let apply f x =
19-
bind (fun f' -> bind (fun x' -> retn (f' x')) x) f
43+
let inline map2
44+
([<InlineIfLambda>] mapper: 'okInput1 -> 'okInput2 -> 'okOutput)
45+
(input1: Result<'okInput1 option, 'error>)
46+
(input2: Result<'okInput2 option, 'error>)
47+
: Result<'okOutput option, 'error> =
48+
match (input1, input2) with
49+
| Ok x, Ok y ->
50+
match x, y with
51+
| Some x', Some y' -> Ok(Some(mapper x' y'))
52+
| _ -> Ok None
53+
| Error e, _
54+
| _, Error e -> Error e
2055

21-
let map2 f x y = (apply (apply (retn f) x) y)
56+
let inline map3
57+
([<InlineIfLambda>] mapper: 'okInput1 -> 'okInput2 -> 'okInput3 -> 'okOutput)
58+
(input1: Result<'okInput1 option, 'error>)
59+
(input2: Result<'okInput2 option, 'error>)
60+
(input3: Result<'okInput3 option, 'error>)
61+
: Result<'okOutput option, 'error> =
62+
match (input1, input2, input3) with
63+
| Ok x, Ok y, Ok z ->
64+
match x, y, z with
65+
| Some x', Some y', Some z' -> Ok(Some(mapper x' y' z'))
66+
| _ -> Ok None
67+
| Error e, _, _
68+
| _, Error e, _
69+
| _, _, Error e -> Error e
2270

23-
let map3 f x y z = apply (map2 f x y) z
71+
let zip
72+
(left: Result<'leftOk option, 'error>)
73+
(right: Result<'rightOk option, 'error>)
74+
: Result<('leftOk * 'rightOk) option, 'error> =
75+
match left, right with
76+
| Ok x1res, Ok x2res -> //Ok(x1res, x2res)
77+
match x1res, x2res with
78+
| Some x1, Some x2 -> Ok(Some(x1, x2))
79+
| _ -> Ok None
80+
| Error e, _ -> Error e
81+
| _, Error e -> Error e
82+
83+
let zipError
84+
(left: Result<'ok option, 'leftError>)
85+
(right: Result<'ok option, 'rightError>)
86+
: Result<'ok option, 'leftError * 'rightError> =
87+
match left, right with
88+
| Error x1res, Error x2res -> Error(x1res, x2res)
89+
| Ok e, _ -> Ok e
90+
| _, Ok e -> Ok e
2491

2592
/// Replaces the wrapped value with unit
26-
let ignore<'ok, 'error> (ro: Result<'ok option, 'error>) =
27-
ro
93+
let inline ignore<'ok, 'error> (resultOpt: Result<'ok option, 'error>) =
94+
resultOpt
2895
|> map ignore<'ok>
96+
97+
let inline ofResult (result: Result<'ok, 'error>) : Result<'ok option, 'error> =
98+
match result with
99+
| Ok x -> Ok(Some x)
100+
| Error e -> Error e
101+
102+
let inline ofOption (option: 'T option) : Result<'T option, 'error> = Ok option
103+
104+
let inline ofChoice (choice: Choice<'ok, 'error>) : Result<'ok option, 'error> =
105+
match choice with
106+
| Choice1Of2 x -> Ok(Some x)
107+
| Choice2Of2 e -> Error e
Lines changed: 134 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,146 @@
11
namespace FsToolkit.ErrorHandling
22

3+
open System
4+
35
[<AutoOpen>]
46
module ResultOptionCE =
57

68
type ResultOptionBuilder() =
7-
member _.Return value = ResultOption.retn value
8-
member _.ReturnFrom value = value
9+
member inline _.Return value = ResultOption.retn value
10+
11+
member inline _.ReturnFrom value : Result<'ok option, 'error> = value
12+
13+
member inline _.Zero() =
14+
option.Zero()
15+
|> Ok
16+
17+
member inline _.Bind
18+
(
19+
resultOpt: Result<'okInput option, 'error>,
20+
[<InlineIfLambda>] binder: 'okInput -> Result<'okOutput option, 'error>
21+
) : Result<'okOutput option, 'error> =
22+
ResultOption.bind binder resultOpt
23+
24+
member inline _.Combine
25+
(
26+
resultOpt: Result<unit option, 'error>,
27+
[<InlineIfLambda>] binder: unit -> Result<'ok option, 'error>
28+
) : Result<'ok option, 'error> =
29+
ResultOption.bind binder resultOpt
30+
31+
member inline _.Delay([<InlineIfLambda>] delayer: unit -> Result<'ok option, 'error>) =
32+
delayer
33+
34+
member inline _.Run
35+
([<InlineIfLambda>] generator: unit -> Result<'ok option, 'error>)
36+
: Result<'ok option, 'error> =
37+
generator ()
38+
39+
member inline this.TryWith
40+
(
41+
[<InlineIfLambda>] generator: unit -> Result<'T option, 'TError>,
42+
[<InlineIfLambda>] handler: exn -> Result<'T option, 'TError>
43+
) : Result<'T option, 'TError> =
44+
try
45+
this.Run generator
46+
with e ->
47+
handler e
48+
49+
member inline this.TryFinally
50+
(
51+
[<InlineIfLambda>] generator: unit -> Result<'ok option, 'error>,
52+
[<InlineIfLambda>] compensation: unit -> unit
53+
) : Result<'ok option, 'error> =
54+
try
55+
this.Run generator
56+
finally
57+
compensation ()
58+
59+
member inline this.Using
60+
(
61+
resource: 'disposable :> IDisposable,
62+
binder: 'disposable -> Result<'ok option, 'error>
63+
) : Result<'ok option, 'error> =
64+
this.TryFinally(
65+
(fun () -> binder resource),
66+
(fun () ->
67+
if not (obj.ReferenceEquals(resource, null)) then
68+
resource.Dispose()
69+
)
70+
)
71+
72+
member inline this.While
73+
(
74+
[<InlineIfLambda>] guard: unit -> bool,
75+
[<InlineIfLambda>] generator: unit -> Result<unit option, 'error>
76+
) : Result<unit option, 'error> =
977

10-
member _.Bind(resultOpt, binder) = ResultOption.bind binder resultOpt
78+
let mutable doContinue = true
79+
let mutable result = Ok(Some())
1180

12-
member _.Combine(r1, r2) =
13-
r1
14-
|> ResultOption.bind (fun _ -> r2)
81+
while doContinue
82+
&& guard () do
83+
match generator () with
84+
| Ok option ->
85+
match option with
86+
| Some _ -> ()
87+
| None ->
88+
doContinue <- false
89+
result <- Ok None
90+
| Error e ->
91+
doContinue <- false
92+
result <- Error e
1593

16-
member _.Delay f = f ()
94+
result
1795

96+
member inline this.For
97+
(
98+
sequence: #seq<'T>,
99+
[<InlineIfLambda>] binder: 'T -> Result<unit option, 'TError>
100+
) : Result<unit option, 'TError> =
101+
this.Using(
102+
sequence.GetEnumerator(),
103+
fun enum ->
104+
this.While(
105+
(fun () -> enum.MoveNext()),
106+
this.Delay(fun () -> binder enum.Current)
107+
)
108+
)
109+
110+
member inline _.BindReturn
111+
(
112+
resultOpt: Result<'T option, 'TError>,
113+
[<InlineIfLambda>] binder: 'T -> 'U
114+
) : Result<'U option, 'TError> =
115+
ResultOption.map binder resultOpt
116+
117+
member inline _.MergeSources
118+
(
119+
left: Result<'left option, 'error>,
120+
right: Result<'right option, 'error>
121+
) : Result<('left * 'right) option, 'error> =
122+
ResultOption.zip left right
123+
124+
member inline _.Source(result: Result<'ok option, 'error>) : Result<'ok option, 'error> =
125+
result
18126

19127
let resultOption = ResultOptionBuilder()
128+
129+
[<AutoOpen>]
130+
module ResultOptionCEExtensions =
131+
132+
type ResultOptionBuilder with
133+
134+
/// <summary>
135+
/// Needed to allow `for..in` and `for..do` functionality
136+
/// </summary>
137+
member inline _.Source(s: #seq<'value>) : #seq<'value> = s
138+
139+
member inline _.Source(result: Result<'ok, 'error>) : Result<'ok option, 'error> =
140+
ResultOption.ofResult result
141+
142+
member inline _.Source(option: 'T option) : Result<'T option, 'error> =
143+
ResultOption.ofOption option
144+
145+
member inline _.Source(choice: Choice<'T, 'Error>) : Result<'T option, 'Error> =
146+
ResultOption.ofChoice choice

tests/FsToolkit.ErrorHandling.Tests/FsToolkit.ErrorHandling.Tests.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
<Compile Include="Result.fs" />
2222
<Compile Include="ResultCE.fs" />
2323
<Compile Include="ResultOption.fs" />
24+
<Compile Include="ResultOptionCE.fs" />
2425
<Compile Include="Option.fs" />
2526
<Compile Include="OptionCE.fs" />
2627
<Compile Include="ValueOption.fs" />

0 commit comments

Comments
 (0)