-
-
Notifications
You must be signed in to change notification settings - Fork 116
/
option.gr
359 lines (338 loc) 路 9.65 KB
/
option.gr
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
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
/**
* Utilities for working with the Option data type.
*
* The Option type is an enum that represents the possibility of something being present (with the `Some` variant), or not (with the `None` variant). There鈥檚 no standalone `null` or `nil` type in Grain; use an Option where you would normally reach for `null` or `nil`.
*
* @example from "option" include Option
*
* @example let hasValue = Some(1234) // Creates an Option containing 1234
* @example let noValue = None // Creates an Option containing nothing
*
* @since v0.2.0
*/
module Option
/**
* Checks if the Option is the `Some` variant.
*
* @param option: The option to check
* @returns `true` if the Option is the `Some` variant or `false` otherwise
*
* @since v0.2.0
*/
provide let isSome = option => {
match (option) {
Some(_) => true,
None => false,
}
}
/**
* Checks if the Option is the `None` variant.
*
* @param option: The option to check
* @returns `true` if the Option is the `None` variant or `false` otherwise
*
* @since v0.2.0
*/
provide let isNone = option => {
match (option) {
None => true,
Some(_) => false,
}
}
/**
* Checks if the Option is the `Some` variant and contains the given value. Uses the generic `==` equality operator.
*
* @param value: The value to search for
* @param option: The option to search
* @returns `true` if the Option is equivalent to `Some(value)` or `false` otherwise
*
* @since v0.2.0
*/
provide let contains = (value, option) => {
match (option) {
Some(x) => x == value,
None => false,
}
}
/**
* Extracts the value inside a `Some` option, otherwise throws an
* exception containing the message provided.
*
* @param msg: The message to use upon failure
* @param option: The option to extract a value from
* @returns The unwrapped value if the Option is the `Some` variant
*
* @throws Failure(String): When the `option` is `None`
*
* @since v0.2.0
*/
provide let expect = (msg, option) => {
match (option) {
Some(x) => x,
None => fail msg,
}
}
/**
* Extracts the value inside a `Some` option, otherwise
* throws an exception containing a default message.
*
* @param option: The option to extract the value from
* @returns The unwrapped value if the Option is the `Some` variant
*
* @throws Failure(String): When the `option` is `None`
*
* @since v0.2.0
*/
provide let unwrap = option => {
expect("Could not unwrap None value", option)
}
/**
* Extracts the value inside a `Some` option or provide the default value if `None`.
*
* @param default: The default value
* @param option: The option to unwrap
* @returns The unwrapped value if the Option is the `Some` variant or the default value otherwise
*
* @since v0.2.0
*/
provide let unwrapWithDefault = (default, option) => {
match (option) {
Some(x) => x,
None => default,
}
}
/**
* If the Option is `Some(value)`, applies the given function to the `value` and wraps the new value in a `Some` variant.
*
* @param fn: The function to call on the value of a `Some` variant
* @param option: The option to map
* @returns A new `Some` variant produced by the mapping function if the variant was `Some` or the unmodified `None` otherwise
*
* @since v0.2.0
*/
provide let map = (fn, option) => {
match (option) {
Some(x) => Some(fn(x)),
None => None,
}
}
/**
* If the Option is `Some(value)`, applies the given function to the `value` to produce a new value, otherwise uses the default value.
* Useful for unwrapping an Option while providing a fallback for any `None` variants.
*
* @param fn: The function to call on the value of a `Some` variant
* @param default: A fallback value for a `None` variant
* @param option: The option to map
* @returns The value produced by the mapping function if the Option is of the `Some` variant or the default value otherwise
*
* @since v0.2.0
*/
provide let mapWithDefault = (fn, default, option) => {
match (option) {
Some(x) => fn(x),
None => default,
}
}
/**
* If the Option is `Some(value)`, applies the `fn` function to the `value` to produce a new value.
* If the Option is `None`, calls the `defaultFn` function to produce a new value.
* Useful for unwrapping an Option into a value, whether it is `Some` or `None`.
*
* @param fn: The function to call on the value of a `Some` variant
* @param defaultFn: The default function
* @param option: The option to map
* @returns The value produced by one of the mapping functions
*
* @since v0.2.0
*/
provide let mapWithDefaultFn = (fn, defaultFn, option) => {
match (option) {
Some(x) => fn(x),
None => defaultFn(),
}
}
/**
* If the Option is `Some(value)`, applies the given function to the `value` to produce a new Option.
*
* @param fn: The function to call on the value of a `Some` variant
* @param option: The option to map
* @returns A new Option produced by the mapping function if the variant was `Some` or the unmodified `None` otherwise
*
* @since v0.2.0
*/
provide let flatMap = (fn, option) => {
match (option) {
Some(x) => fn(x),
None => None,
}
}
/**
* Converts `Some(value)` variants to `None` variants where the predicate function returns `false`.
* if the `fn` return `true` returns `Some(value)`, otherwise returns `None`.
*
* @param fn: The predicate function to indicate if the option should remain `Some`
* @param option: The option to inspect
* @returns `Some(value)` if the variant was `Some` and the predicate returns `true` or `None` otherwise
*
* @since v0.2.0
*/
provide let filter = (fn, option) => {
match (option) {
Some(x) => if (fn(x)) {
Some(x)
} else {
None
},
None => None,
}
}
/**
* Combine two Options into a single Option containing a tuple of their values.
*
* @param optionA: The first option to combine
* @param optionB: The second option to combine
* @returns `Some((valueA, valueB))` if both Options are `Some` variants or `None` otherwise
*
* @since v0.2.0
*/
provide let zip = (optionA, optionB) => {
match ((optionA, optionB)) {
(Some(a), Some(b)) => Some((a, b)),
_ => None,
}
}
/**
* Combine two Options into a single Option. The new value is produced by applying the given function to both values.
*
* @param fn: The function to generate a new value
* @param optionA: The first option to combine
* @param optionB: The second option to combine
* @returns `Some(newValue)` if both Options are `Some` variants or `None` otherwise
*
* @since v0.2.0
*/
provide let zipWith = (fn, optionA, optionB) => {
match ((optionA, optionB)) {
(Some(a), Some(b)) => Some(fn(a, b)),
_ => None,
}
}
/**
* Flattens nested Options.
*
* @param option: The option to flatten
* @returns `Some(innerValue)` if all nested options were the `Some` variant or `None` otherwise
*
* @example Option.flatten(Some(Some(1))) == Some(1)
*
* @since v0.2.0
*/
provide let flatten = option => {
match (option) {
Some(Some(x)) => Some(x),
_ => None,
}
}
/**
* Converts an Option to a list with either zero or one item.
*
* @param option: The option to convert
* @returns `[value]` if the Option was the `Some` variant or `[]` otherwise
*
* @since v0.2.0
*/
provide let toList = option => {
match (option) {
Some(x) => [x],
None => [],
}
}
/**
* Converts an Option to an array with either zero or one item.
*
* @param option: The option to convert
* @returns `[> value]` if the Option was the `Some` variant or `[> ]` otherwise
*
* @since v0.2.0
*/
provide let toArray = option => {
match (option) {
Some(x) => [> x],
None => [>],
}
}
/**
* Converts the Option to a Result, using the provided error in case of the `None` variant.
*
* @param err: The error to use if the option is `None`
* @param option: The option to convert
* @returns `Ok(value)` if the Option is `Some(value)` or `Err(err)` if the Option is `None`
*
* @since v0.2.0
*/
provide let toResult = (err, option) => {
match (option) {
Some(a) => Ok(a),
None => Err(err),
}
}
/**
* If the Option is `Some(value)`, applies the `fn` function to the `value` without producing a new value.
*
* @param fn: The function to call on the value of a `Some` variant
* @param option: The option to inspect
*
* @since v0.2.0
*/
provide let sideEffect = (fn, option) => {
match (option) {
Some(x) => fn(x),
None => void,
}
}
/**
* If the Option is `Some(value)`, applies the `fn` function to the `value` without producing a new value.
* Useful for inspecting Options without changing anything.
*
* @param fn: The function to call on the value of a `Some` variant
* @param option: The option to inspect
* @returns The unmodified option
*
* @since v0.2.0
*/
provide let peek = (fn, option) => {
sideEffect(fn, option)
option
}
/**
* Behaves like a logical OR (`||`) where the first Option is only returned if it is the `Some` variant and falling back to the second Option in all other cases.
*
* @param optionA: The first option
* @param optionB: The second option
* @returns The first Option if it is the `Some` variant or the second Option otherwise
*
* @since v0.6.0
* @history v0.2.0: Originally named `or`
*/
provide let (||) = (optionA, optionB) => {
match (optionA) {
Some(x) => optionA,
None => optionB,
}
}
/**
* Behaves like a logical AND (`&&`) where the first Option is only returned if it is the `None` variant and falling back to the second Option Result in all other cases.
*
* @param optionA: The first option
* @param optionB: The second option
* @returns The second Option if both are the `Some` variant or the first Option otherwise
*
* @since v0.6.0
* @history v0.2.0: Originally named `and`
*/
provide let (&&) = (optionA, optionB) => {
match (optionA) {
Some(_) => optionB,
None => optionA,
}
}