-
Notifications
You must be signed in to change notification settings - Fork 359
/
Copy pathResult.elm
286 lines (217 loc) · 6.27 KB
/
Result.elm
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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
module Result exposing
( Result(..)
, withDefault
, map, map2, map3, map4, map5
, andThen
, toMaybe, fromMaybe, mapError
)
{-| A `Result` is the result of a computation that may fail. This is a great
way to manage errors in Elm.
# Type and Constructors
@docs Result
# Mapping
@docs map, map2, map3, map4, map5
# Chaining
@docs andThen
# Handling Errors
@docs withDefault, toMaybe, fromMaybe, mapError
-}
import Basics exposing ( Bool(..) )
import Maybe exposing ( Maybe(..) )
{-| A `Result` is either `Ok` meaning the computation succeeded, or it is an
`Err` meaning that there was some failure.
-}
type Result error value
= Ok value
| Err error
{-| If the result is `Ok` return the value, but if the result is an `Err` then
return a given default value. The following examples try to parse integers.
Result.withDefault 0 (Ok 123) == 123
Result.withDefault 0 (Err "no") == 0
-}
withDefault : a -> Result x a -> a
withDefault def result =
case result of
Ok a ->
a
Err _ ->
def
{-| Apply a function to a result. If the result is `Ok`, it will be converted.
If the result is an `Err`, the same error value will propagate through.
map sqrt (Ok 4.0) == Ok 2.0
map sqrt (Err "bad input") == Err "bad input"
-}
map : (a -> value) -> Result x a -> Result x value
map func ra =
case ra of
Ok a ->
Ok (func a)
Err e ->
Err e
{-| Apply a function if both results are `Ok`. If not, the first `Err` will
propagate through.
map2 max (Ok 42) (Ok 13) == Ok 42
map2 max (Err "x") (Ok 13) == Err "x"
map2 max (Ok 42) (Err "y") == Err "y"
map2 max (Err "x") (Err "y") == Err "x"
This can be useful if you have two computations that may fail, and you want
to put them together quickly.
-}
map2 : (a -> b -> value) -> Result x a -> Result x b -> Result x value
map2 func ra rb =
case ra of
Err x ->
Err x
Ok a ->
case rb of
Err x ->
Err x
Ok b ->
Ok (func a b)
{-|-}
map3 : (a -> b -> c -> value) -> Result x a -> Result x b -> Result x c -> Result x value
map3 func ra rb rc =
case ra of
Err x ->
Err x
Ok a ->
case rb of
Err x ->
Err x
Ok b ->
case rc of
Err x ->
Err x
Ok c ->
Ok (func a b c)
{-|-}
map4 : (a -> b -> c -> d -> value) -> Result x a -> Result x b -> Result x c -> Result x d -> Result x value
map4 func ra rb rc rd =
case ra of
Err x ->
Err x
Ok a ->
case rb of
Err x ->
Err x
Ok b ->
case rc of
Err x ->
Err x
Ok c ->
case rd of
Err x ->
Err x
Ok d ->
Ok (func a b c d)
{-|-}
map5 : (a -> b -> c -> d -> e -> value) -> Result x a -> Result x b -> Result x c -> Result x d -> Result x e -> Result x value
map5 func ra rb rc rd re =
case ra of
Err x ->
Err x
Ok a ->
case rb of
Err x ->
Err x
Ok b ->
case rc of
Err x ->
Err x
Ok c ->
case rd of
Err x ->
Err x
Ok d ->
case re of
Err x ->
Err x
Ok e ->
Ok (func a b c d e)
{-| Chain together a sequence of computations that may fail. It is helpful
to see its definition:
andThen : (a -> Result e b) -> Result e a -> Result e b
andThen callback result =
case result of
Ok value -> callback value
Err msg -> Err msg
This means we only continue with the callback if things are going well. For
example, say you need to use (`toInt : String -> Result String Int`) to parse
a month and make sure it is between 1 and 12:
toValidMonth : Int -> Result String Int
toValidMonth month =
if month >= 1 && month <= 12
then Ok month
else Err "months must be between 1 and 12"
toMonth : String -> Result String Int
toMonth rawString =
toInt rawString
|> andThen toValidMonth
-- toMonth "4" == Ok 4
-- toMonth "9" == Ok 9
-- toMonth "a" == Err "cannot parse to an Int"
-- toMonth "0" == Err "months must be between 1 and 12"
This allows us to come out of a chain of operations with quite a specific error
message. It is often best to create a custom type that explicitly represents
the exact ways your computation may fail. This way it is easy to handle in your
code.
-}
andThen : (a -> Result x b) -> Result x a -> Result x b
andThen callback result =
case result of
Ok value ->
callback value
Err msg ->
Err msg
{-| Transform an `Err` value. For example, say the errors we get have too much
information:
parseInt : String -> Result ParseError Int
type alias ParseError =
{ message : String
, code : Int
, position : (Int,Int)
}
mapError .message (parseInt "123") == Ok 123
mapError .message (parseInt "abc") == Err "char 'a' is not a number"
-}
mapError : (x -> y) -> Result x a -> Result y a
mapError f result =
case result of
Ok v ->
Ok v
Err e ->
Err (f e)
{-| Convert to a simpler `Maybe` if the actual error message is not needed or
you need to interact with some code that primarily uses maybes.
parseInt : String -> Result ParseError Int
maybeParseInt : String -> Maybe Int
maybeParseInt string =
toMaybe (parseInt string)
-}
toMaybe : Result x a -> Maybe a
toMaybe result =
case result of
Ok v -> Just v
Err _ -> Nothing
{-| Convert from a simple `Maybe` to interact with some code that primarily
uses `Results`.
parseInt : String -> Maybe Int
resultParseInt : String -> Result String Int
resultParseInt string =
fromMaybe ("error parsing string: " ++ toString string) (parseInt string)
-}
fromMaybe : x -> Maybe a -> Result x a
fromMaybe err maybe =
case maybe of
Just v -> Ok v
Nothing -> Err err
-- FOR INTERNAL USE ONLY
--
-- Use `case` expressions for this in Elm code!
isOk : Result x a -> Bool
isOk result =
case result of
Ok _ ->
True
Err _ ->
False