-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathRop.fs
220 lines (175 loc) · 5.84 KB
/
Rop.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
[<AutoOpen>]
module NotaFiscal.Domain.Rop
open System.Threading.Tasks
open System
[<AutoOpen>]
module Rop =
type OperationResult<'TSuccess, 'TErrorMessage> =
| Success of 'TSuccess * 'TErrorMessage list
| Failure of 'TErrorMessage list
let succeed x = Success(x, [])
let fail msg = Failure([ msg ])
let failures msgs = Failure(msgs)
let teeR f result =
match result with
| Success(v, msgs) ->
let result' = f v
match result' with
| Success(v, r) -> Success(v, r @ msgs)
| Failure(msg') -> Failure(msg' @ msgs)
| Failure(msgs) -> Failure(msgs)
let bindR result f : OperationResult<'a, 'b> =
match result with
| Success(v, msgs) ->
let result' = f v
match result' with
| Success(v, r) -> Success(v, r @ msgs)
| Failure(msg') -> Failure(msg' @ msgs)
| Failure(msgs) -> Failure(msgs)
let mapR f result =
match result with
| Success(v, msg) -> Success(f v, msg)
| Failure(msgs) -> Failure(msgs)
let mapMsgR f result =
match result with
| Success(v, msgs) -> Success(v, [ f v ] @ msgs)
| Failure(msgs) -> Failure(msgs)
let applyR
(resultOfFunc: OperationResult<('a -> 'b), 'c>)
(result: OperationResult<'a, 'c>)
: OperationResult<'b, 'c>
=
match resultOfFunc, result with
| Success(f, msgs1), Success(x, msgs2) ->
(f x, msgs1 @ msgs2) |> Success
| Failure errs, Success(_, msgs)
| Success(_, msgs), Failure errs -> errs @ msgs |> Failure
| Failure errs1, Failure errs2 -> errs1 @ errs2 |> Failure
let rec traverseResultA
(f: 'a -> OperationResult<'b, 'c>)
(list: 'a list)
: OperationResult<'b list, 'c>
=
// define the applicative functions
let (<*>) = applyR
let retn =
function
| x -> Success(x, [])
// define a "cons" function
let cons head tail = head :: tail
// loop through the list
match list with
| [] ->
// if empty, lift [] to a Result
retn []
| head :: tail ->
// otherwise lift the head to a Result using f
// and cons it with the lifted version of the remaining list
retn cons <*> (f head) <*> (traverseResultA f tail)
let mapFailuresR
(f: 'a -> 'b)
(result: OperationResult<'c, 'a>)
: OperationResult<'c, 'b>
=
match result with
| Success(v, msg) ->
let msgs = List.map f msg
Success(v, msgs)
| Failure(msgs) ->
let msgs = List.map f msgs
Failure(msgs)
let aggregateFailuresR (results: OperationResult<'a, 'b> list) : 'b list =
List.map
(function
| Success _ -> []
| Failure err -> err)
results
|> List.concat
let strToOptionStr value =
match value with
| v when String.IsNullOrWhiteSpace v -> None
| _ -> Some value
let (<*>) = applyR
let (<!>) = mapR
let (>>=) = bindR
let isFailure result =
match result with
| Success _ -> false
| Failure(_) -> true
let toResult fResult err =
match fResult with
| Ok v -> succeed v
| Error _ -> fail err
let hasAnyFailure results = results |> List.exists isFailure
let failIfNoneR msg value =
match value with
| None -> fail msg
| Some x -> succeed x
let traverseResult f value =
match value with
| None -> succeed None
| Some x -> (succeed Some) <*> (f x)
let mapSuccessResults results =
List.map
(function
| Success(v, _) -> [ v ]
| Failure(_) -> [])
results
|> List.concat
let mapNullToOptR value f =
match box value with
| null -> succeed None
| _ -> f value
let mapNullToR value f err =
match box value with
| null -> fail err
| _ -> f value
type ResultBuilder() =
member this.Return x = succeed x
member this.Bind(xResult, f) = bindR xResult f
let result = ResultBuilder()
module Option =
let withDefaultNullable option =
option |> Option.map Nullable |> Option.defaultWith Nullable
[<AutoOpen>]
module AsyncRop =
let bindRAsync
(result: OperationResult<'a, 'b>)
(f: 'a -> Task<OperationResult<'c, 'b>>)
=
match result with
| Success(v, _) ->
task {
let! result' = f v
return
match result' with
| Success(v, r) -> Success(v, r)
| Failure(msg') -> Failure(msg')
}
| Failure(msgs) -> Failure(msgs) |> Task.FromResult
let mapRAsync resultRAsync f =
task {
let! resultR = resultRAsync
return mapR f resultR
}
let checkRAsync resultR f =
match resultR with
| Success(v, msgs) ->
task {
let! result' = f v
return
match result' with
| Success(_, r) -> Success(v, r @ msgs)
| Failure(msg') -> Failure(msg' @ msgs)
}
| Failure(msgs) -> Failure(msgs) |> Task.FromResult
let mapFailuresRAsync
(f: 'a -> 'b)
(result: Task<OperationResult<'c, 'a>>)
: Task<OperationResult<'c, 'b>>
=
task {
let! taskResult = result
return mapFailuresR f taskResult
}
let (>>=) = bindRAsync