@grain/stdlib 0.4.3 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/result.md ADDED
@@ -0,0 +1,446 @@
1
+ ---
2
+ title: Result
3
+ ---
4
+
5
+ Utilities for working with the Result data type.
6
+
7
+ The Result type is an enum that represents the possibility of a success case (with the `Ok` variant),
8
+ or an error case (with the `Err` variant). Use a Result as the return type of a function that may return an error.
9
+
10
+ <details disabled>
11
+ <summary tabindex="-1">Added in <code>0.2.0</code></summary>
12
+ No other changes yet.
13
+ </details>
14
+
15
+ ```grain
16
+ import Result from "result"
17
+ ```
18
+
19
+ ```grain
20
+ let success = Ok((x) => 1 + x) // Creates a successful Result containing (x) => 1 + x
21
+ ```
22
+
23
+ ```grain
24
+ let failure = Err("Something bad happened") // Creates an unsuccessful Result containing "Something bad happened"
25
+ ```
26
+
27
+ ## Values
28
+
29
+ Functions for working with the Result data type.
30
+
31
+ ### Result.**isOk**
32
+
33
+ <details disabled>
34
+ <summary tabindex="-1">Added in <code>0.2.0</code></summary>
35
+ No other changes yet.
36
+ </details>
37
+
38
+ ```grain
39
+ isOk : Result<a, b> -> Bool
40
+ ```
41
+
42
+ Checks if the Result is the `Ok` variant.
43
+
44
+ Parameters:
45
+
46
+ |param|type|description|
47
+ |-----|----|-----------|
48
+ |`result`|`Result<a, b>`|The result to check|
49
+
50
+ Returns:
51
+
52
+ |type|description|
53
+ |----|-----------|
54
+ |`Bool`|`true` if the Result is the `Ok` variant or `false` otherwise|
55
+
56
+ ### Result.**isErr**
57
+
58
+ <details disabled>
59
+ <summary tabindex="-1">Added in <code>0.2.0</code></summary>
60
+ No other changes yet.
61
+ </details>
62
+
63
+ ```grain
64
+ isErr : Result<a, b> -> Bool
65
+ ```
66
+
67
+ Checks if the Result is the `Err` variant.
68
+
69
+ Parameters:
70
+
71
+ |param|type|description|
72
+ |-----|----|-----------|
73
+ |`result`|`Result<a, b>`|The result to check|
74
+
75
+ Returns:
76
+
77
+ |type|description|
78
+ |----|-----------|
79
+ |`Bool`|`true` if the Result is the `Err` variant or `false` otherwise|
80
+
81
+ ### Result.**toOption**
82
+
83
+ <details disabled>
84
+ <summary tabindex="-1">Added in <code>0.2.0</code></summary>
85
+ No other changes yet.
86
+ </details>
87
+
88
+ ```grain
89
+ toOption : Result<a, b> -> Option<a>
90
+ ```
91
+
92
+ Converts the Result to an Option. An error value is discarded and replaced with `None`.
93
+
94
+ Parameters:
95
+
96
+ |param|type|description|
97
+ |-----|----|-----------|
98
+ |`result`|`Result<a, b>`|The result to convert|
99
+
100
+ Returns:
101
+
102
+ |type|description|
103
+ |----|-----------|
104
+ |`Option<a>`|`Some(value)` if the Result is `Ok(value)` or `None` if the Result is an `Err`|
105
+
106
+ ### Result.**flatMap**
107
+
108
+ <details disabled>
109
+ <summary tabindex="-1">Added in <code>0.2.0</code></summary>
110
+ No other changes yet.
111
+ </details>
112
+
113
+ ```grain
114
+ flatMap : ((a -> Result<b, c>), Result<a, c>) -> Result<b, c>
115
+ ```
116
+
117
+ If the Result is `Ok(value)`, applies the given function to the `value` to produce a new Result.
118
+
119
+ Parameters:
120
+
121
+ |param|type|description|
122
+ |-----|----|-----------|
123
+ |`fn`|`a -> Result<b, c>`|The function to call on the value of an `Ok` variant|
124
+ |`result`|`Result<a, c>`|The result to map|
125
+
126
+ Returns:
127
+
128
+ |type|description|
129
+ |----|-----------|
130
+ |`Result<b, c>`|A new Result produced by the mapping function if the variant was `Ok` or the unmodified `Err` otherwise|
131
+
132
+ ### Result.**flatMapErr**
133
+
134
+ <details disabled>
135
+ <summary tabindex="-1">Added in <code>0.2.0</code></summary>
136
+ No other changes yet.
137
+ </details>
138
+
139
+ ```grain
140
+ flatMapErr : ((a -> Result<b, c>), Result<b, a>) -> Result<b, c>
141
+ ```
142
+
143
+ If the Result is an `Err(value)`, applies the given function to the `value` to produce a new Result.
144
+
145
+ Parameters:
146
+
147
+ |param|type|description|
148
+ |-----|----|-----------|
149
+ |`fn`|`a -> Result<b, c>`|The function to call on the value of an `Err` variant|
150
+ |`result`|`Result<b, a>`|The result to map|
151
+
152
+ Returns:
153
+
154
+ |type|description|
155
+ |----|-----------|
156
+ |`Result<b, c>`|A new Result produced by the mapping function if the variant was `Err` or the unmodified `Ok` otherwise|
157
+
158
+ ### Result.**map**
159
+
160
+ <details disabled>
161
+ <summary tabindex="-1">Added in <code>0.2.0</code></summary>
162
+ No other changes yet.
163
+ </details>
164
+
165
+ ```grain
166
+ map : ((a -> b), Result<a, c>) -> Result<b, c>
167
+ ```
168
+
169
+ If the Result is `Ok(value)`, applies the given function to the `value` and wraps the new value in an `Ok` variant.
170
+
171
+ Parameters:
172
+
173
+ |param|type|description|
174
+ |-----|----|-----------|
175
+ |`fn`|`a -> b`|The function to call on the value of an `Ok` variant|
176
+ |`result`|`Result<a, c>`|The result to map|
177
+
178
+ Returns:
179
+
180
+ |type|description|
181
+ |----|-----------|
182
+ |`Result<b, c>`|A new `Ok` variant produced by the mapping function if the variant was `Ok` or the unmodified `Err` otherwise|
183
+
184
+ ### Result.**mapErr**
185
+
186
+ <details disabled>
187
+ <summary tabindex="-1">Added in <code>0.2.0</code></summary>
188
+ No other changes yet.
189
+ </details>
190
+
191
+ ```grain
192
+ mapErr : ((a -> b), Result<c, a>) -> Result<c, b>
193
+ ```
194
+
195
+ If the Result is `Err(value)`, applies the given function to the `value` and wraps the new value in an `Err` variant.
196
+
197
+ Parameters:
198
+
199
+ |param|type|description|
200
+ |-----|----|-----------|
201
+ |`fn`|`a -> b`|The function to call on the value of an `Err` variant|
202
+ |`result`|`Result<c, a>`|The result to map|
203
+
204
+ Returns:
205
+
206
+ |type|description|
207
+ |----|-----------|
208
+ |`Result<c, b>`|A new `Err` variant produced by the mapping function if the variant was `Err` or the unmodified `Ok` otherwise|
209
+
210
+ ### Result.**mapWithDefault**
211
+
212
+ <details disabled>
213
+ <summary tabindex="-1">Added in <code>0.2.0</code></summary>
214
+ No other changes yet.
215
+ </details>
216
+
217
+ ```grain
218
+ mapWithDefault : ((a -> b), b, Result<a, c>) -> b
219
+ ```
220
+
221
+ If the Result is `Ok(value)`, applies the given function to the `value` to produce a new value, otherwise uses the default value.
222
+ Useful for unwrapping a successful Result while providing a fallback for any errors that can occur.
223
+
224
+ Parameters:
225
+
226
+ |param|type|description|
227
+ |-----|----|-----------|
228
+ |`fn`|`a -> b`|The function to call on the value of an `Ok` variant|
229
+ |`def`|`b`|A fallback value for an `Err` variant|
230
+ |`result`|`Result<a, c>`|The result to map|
231
+
232
+ Returns:
233
+
234
+ |type|description|
235
+ |----|-----------|
236
+ |`b`|The value produced by the mapping function if the result is of the `Ok` variant or the default value otherwise|
237
+
238
+ ### Result.**mapWithDefaultFn**
239
+
240
+ <details disabled>
241
+ <summary tabindex="-1">Added in <code>0.2.0</code></summary>
242
+ No other changes yet.
243
+ </details>
244
+
245
+ ```grain
246
+ mapWithDefaultFn : ((a -> b), (c -> b), Result<a, c>) -> b
247
+ ```
248
+
249
+ If the Result is `Ok(value)`, applies the `fnOk` function to the `value` to produce a new value.
250
+ If the Result is `Err(value)`, applies the `fnErr` function to the `value` to produce a new value.
251
+ Useful for unwrapping a Result into a value, whether it is successful or unsuccessful.
252
+
253
+ Parameters:
254
+
255
+ |param|type|description|
256
+ |-----|----|-----------|
257
+ |`fnOk`|`a -> b`|The function to call on the value of an `Ok` variant|
258
+ |`fnErr`|`c -> b`|The function to call on the value of an `Err` variant|
259
+ |`result`|`Result<a, c>`|The result to map|
260
+
261
+ Returns:
262
+
263
+ |type|description|
264
+ |----|-----------|
265
+ |`b`|The value produced by one of the mapping functions|
266
+
267
+ ### Result.**or**
268
+
269
+ <details disabled>
270
+ <summary tabindex="-1">Added in <code>0.2.0</code></summary>
271
+ No other changes yet.
272
+ </details>
273
+
274
+ ```grain
275
+ ( or ) : (Result<a, b>, Result<a, b>) -> Result<a, b>
276
+ ```
277
+
278
+ Behaves like a logical OR (`||`) where the first Result is only returned if it is the `Ok` variant and falling back to the second Result in all other cases.
279
+
280
+ Parameters:
281
+
282
+ |param|type|description|
283
+ |-----|----|-----------|
284
+ |`result1`|`Result<a, b>`|The first result|
285
+ |`result2`|`Result<a, b>`|The second result|
286
+
287
+ Returns:
288
+
289
+ |type|description|
290
+ |----|-----------|
291
+ |`Result<a, b>`|The first Result if it is the `Ok` variant or the second Result otherwise|
292
+
293
+ ### Result.**and**
294
+
295
+ <details disabled>
296
+ <summary tabindex="-1">Added in <code>0.2.0</code></summary>
297
+ No other changes yet.
298
+ </details>
299
+
300
+ ```grain
301
+ and : (Result<a, b>, Result<a, b>) -> Result<a, b>
302
+ ```
303
+
304
+ Behaves like a logical AND (`&&`) where the first Result is only returned if it is the `Err` variant and falling back to the second Result in all other cases.
305
+
306
+ Parameters:
307
+
308
+ |param|type|description|
309
+ |-----|----|-----------|
310
+ |`result1`|`Result<a, b>`|The first result|
311
+ |`result2`|`Result<a, b>`|The second result|
312
+
313
+ Returns:
314
+
315
+ |type|description|
316
+ |----|-----------|
317
+ |`Result<a, b>`|The second Result if both are the `Ok` variant or the first Result otherwise|
318
+
319
+ ### Result.**peek**
320
+
321
+ <details disabled>
322
+ <summary tabindex="-1">Added in <code>0.2.0</code></summary>
323
+ No other changes yet.
324
+ </details>
325
+
326
+ ```grain
327
+ peek : ((a -> b), (c -> d), Result<a, c>) -> Void
328
+ ```
329
+
330
+ If the Result is `Ok(value)`, applies the `fnOk` function to the `value` without producing a new value.
331
+ If the Result is `Err(value)`, applies the `fnErr` function to the `value` without producing a new value.
332
+ Useful for inspecting Results without changing anything.
333
+
334
+ Parameters:
335
+
336
+ |param|type|description|
337
+ |-----|----|-----------|
338
+ |`fnOk`|`a -> b`|The function to call on the value of an `Ok` variant|
339
+ |`fnErr`|`c -> d`|The function to call on the value of an `Err` variant|
340
+ |`result`|`Result<a, c>`|The result to inspect|
341
+
342
+ ### Result.**peekOk**
343
+
344
+ <details disabled>
345
+ <summary tabindex="-1">Added in <code>0.2.0</code></summary>
346
+ No other changes yet.
347
+ </details>
348
+
349
+ ```grain
350
+ peekOk : ((a -> b), Result<a, c>) -> Void
351
+ ```
352
+
353
+ If the Result is `Ok(value)`, applies the given function to the `value` without producing a new value.
354
+
355
+ Parameters:
356
+
357
+ |param|type|description|
358
+ |-----|----|-----------|
359
+ |`fn`|`a -> b`|The function to call on the value of an `Ok` variant|
360
+ |`result`|`Result<a, c>`|The result to inspect|
361
+
362
+ ### Result.**peekErr**
363
+
364
+ <details disabled>
365
+ <summary tabindex="-1">Added in <code>0.2.0</code></summary>
366
+ No other changes yet.
367
+ </details>
368
+
369
+ ```grain
370
+ peekErr : ((a -> b), Result<c, a>) -> Void
371
+ ```
372
+
373
+ If the Result is `Err(value)`, applies the given function to the `value` without producing a new value.
374
+
375
+ Parameters:
376
+
377
+ |param|type|description|
378
+ |-----|----|-----------|
379
+ |`fn`|`a -> b`|The function to call on the value of an `Err` variant|
380
+ |`result`|`Result<c, a>`|The result to inspect|
381
+
382
+ ### Result.**expect**
383
+
384
+ <details disabled>
385
+ <summary tabindex="-1">Added in <code>0.4.0</code></summary>
386
+ No other changes yet.
387
+ </details>
388
+
389
+ ```grain
390
+ expect : (String, Result<a, b>) -> a
391
+ ```
392
+
393
+ Extracts the value inside an `Ok` result, otherwise throw an
394
+ exception containing the message and contents of the `Err`.
395
+
396
+ Parameters:
397
+
398
+ |param|type|description|
399
+ |-----|----|-----------|
400
+ |`msg`|`String`|The message to prepend if the result contains an `Err`|
401
+ |`result`|`Result<a, b>`|The result to extract a value from|
402
+
403
+ Returns:
404
+
405
+ |type|description|
406
+ |----|-----------|
407
+ |`a`|The unwrapped value if the Result is the `Ok` variant|
408
+
409
+ Examples:
410
+
411
+ ```grain
412
+ Result.expect("Unexpected error", Ok(1234)) + 42
413
+ ```
414
+
415
+ ### Result.**unwrap**
416
+
417
+ <details disabled>
418
+ <summary tabindex="-1">Added in <code>0.4.0</code></summary>
419
+ No other changes yet.
420
+ </details>
421
+
422
+ ```grain
423
+ unwrap : Result<a, b> -> a
424
+ ```
425
+
426
+ Extracts the value inside an `Ok` result, otherwise throw an
427
+ exception containing a default message and contents of the `Err`.
428
+
429
+ Parameters:
430
+
431
+ |param|type|description|
432
+ |-----|----|-----------|
433
+ |`result`|`Result<a, b>`|The result to extract a value from|
434
+
435
+ Returns:
436
+
437
+ |type|description|
438
+ |----|-----------|
439
+ |`a`|The unwrapped value if the result is the `Ok` variant|
440
+
441
+ Examples:
442
+
443
+ ```grain
444
+ Result.unwrap(Err("This will throw"))
445
+ ```
446
+
@@ -0,0 +1,172 @@
1
+ // TODO(#1050): Remove dependency on Pervasives once Option/Result types are imbedded in the compiler
2
+
3
+ import WasmI32, {
4
+ add as (+),
5
+ sub as (-),
6
+ gtU as (>),
7
+ geU as (>=),
8
+ ltU as (<),
9
+ shrS as (>>),
10
+ eq as (==),
11
+ ne as (!=),
12
+ and as (&),
13
+ } from "runtime/unsafe/wasmi32"
14
+ import WasmI64 from "runtime/unsafe/wasmi64"
15
+ import Memory from "runtime/unsafe/memory"
16
+ import Tags from "runtime/unsafe/tags"
17
+ import { reducedInteger } from "runtime/numbers"
18
+
19
+ @disableGC
20
+ export let rec parseInt = (string: String, radix: Number) => {
21
+ let _CHAR_0 = 0x30n
22
+ let _CHAR_B = 0x42n
23
+ let _CHAR_b = 0x62n
24
+ let _CHAR_O = 0x4fn
25
+ let _CHAR_o = 0x6fn
26
+ let _CHAR_X = 0x58n
27
+ let _CHAR_x = 0x78n
28
+
29
+ let _CHAR_A = 0x41n
30
+ let _CHAR_a = 0x61n
31
+
32
+ let _CHAR_UNDERSCORE = 0x5fn
33
+ let _CHAR_MINUS = 0x2dn
34
+
35
+ let _INT_MIN = -9223372036854775808N
36
+
37
+ // Don't need to process Unicode length since if the string
38
+ // contains non-ascii characters, it's not a valid integer
39
+ let strLen = WasmI32.load(WasmI32.fromGrain(string), 4n)
40
+
41
+ // Our pointer within the string we're parsing, offset by the
42
+ // header
43
+ let mut offset = WasmI32.fromGrain(string) + 8n
44
+
45
+ let strEnd = offset + strLen
46
+
47
+ let radix = WasmI32.fromGrain(radix)
48
+ let result = if (WasmI32.eqz(radix & Tags._GRAIN_NUMBER_TAG_MASK) || radix < WasmI32.fromGrain(2) || radix > WasmI32.fromGrain(36)) {
49
+ Memory.incRef(WasmI32.fromGrain(Err))
50
+ Err("Radix must be an integer between 2 and 36")
51
+ } else if (WasmI32.eqz(strLen)) {
52
+ Memory.incRef(WasmI32.fromGrain(Err))
53
+ Err("Invalid input")
54
+ } else {
55
+ let mut char = WasmI32.load8U(offset, 0n)
56
+
57
+ let mut limit = WasmI64.add(_INT_MIN, 1N)
58
+
59
+ // Check for a sign
60
+ let mut negative = false
61
+ if (char == _CHAR_MINUS) {
62
+ negative = true
63
+ offset += 1n
64
+ limit = _INT_MIN
65
+ char = WasmI32.load8U(offset, 0n)
66
+ }
67
+
68
+ let mut radix = WasmI64.extendI32S(radix >> 1n)
69
+
70
+ // Check if we should override the supplied radix
71
+ if (char == _CHAR_0 && strLen > 2n) {
72
+ match (WasmI32.load8U(offset, 1n)) {
73
+ c when c == _CHAR_B || c == _CHAR_b => {
74
+ radix = 2N
75
+ offset += 2n
76
+ },
77
+ c when c == _CHAR_O || c == _CHAR_o => {
78
+ radix = 8N
79
+ offset += 2n
80
+ },
81
+ c when c == _CHAR_X || c == _CHAR_x => {
82
+ radix = 16N
83
+ offset += 2n
84
+ },
85
+ _ => void,
86
+ }
87
+ }
88
+
89
+ let mut value = 0N
90
+
91
+ // we use 0 to represent no error, 1 to represent an invalid
92
+ // input, and 2 to represent an overflow
93
+ let mut error = 1n
94
+
95
+ for (let mut i = offset; i < strEnd; i += 1n) {
96
+ let char = WasmI32.load8U(i, 0n)
97
+
98
+ // Ignore underscore characters
99
+ if (char == _CHAR_UNDERSCORE) {
100
+ continue
101
+ }
102
+
103
+ // We've seen at least one non-underscore character, so we'll consider
104
+ // the input valid until we find out otherwise
105
+
106
+ error = 0n
107
+
108
+ let mut digit = 0n
109
+
110
+ match (char) {
111
+ c when c - _CHAR_0 < 10n => digit = char - _CHAR_0,
112
+ c when c - _CHAR_A < 26n => digit = char - _CHAR_A + 10n,
113
+ c when c - _CHAR_a < 26n => digit = char - _CHAR_a + 10n,
114
+ _ => {
115
+ error = 1n
116
+ // invalid digit
117
+ break
118
+ },
119
+ }
120
+
121
+ if (digit >= WasmI32.wrapI64(radix)) {
122
+ error = 1n
123
+ // invalid digit
124
+ break
125
+ }
126
+
127
+ let digit = WasmI64.extendI32U(digit)
128
+
129
+ value = WasmI64.mul(value, radix)
130
+
131
+ // Check for overflow
132
+ // 64-bit int min + 1
133
+ if (WasmI64.ltS(value, WasmI64.add(limit, digit))) {
134
+ error = 2n
135
+ // overflow
136
+ break
137
+ }
138
+
139
+ // To quote the OpenJDK,
140
+ // "Accumulating negatively avoids surprises near MAX_VALUE"
141
+ // The minimum value of a 64-bit integer (-9223372036854775808) can't be
142
+ // represented as a positive number because it would be larger than the
143
+ // maximum 64-bit integer (9223372036854775807), so we'd be unable to
144
+ // parse negatives as positives and multiply by the sign at the end.
145
+ // Instead, we represent all positive numbers as negative numbers since
146
+ // we have one unit more headroom.
147
+ value = WasmI64.sub(value, digit)
148
+ }
149
+
150
+ match (error) {
151
+ 1n => {
152
+ Memory.incRef(WasmI32.fromGrain(Err))
153
+ Err("Invalid digit in input")
154
+ },
155
+ 2n => {
156
+ Memory.incRef(WasmI32.fromGrain(Err))
157
+ Err("Input out of range of representable integers")
158
+ },
159
+ _ => {
160
+ let value = if (negative) value else WasmI64.mul(value, -1N)
161
+ let number = WasmI32.toGrain(reducedInteger(value)): (Number)
162
+ Memory.incRef(WasmI32.fromGrain(Ok))
163
+ Ok(number)
164
+ },
165
+ }
166
+ }
167
+
168
+ Memory.decRef(WasmI32.fromGrain(parseInt))
169
+ Memory.decRef(radix)
170
+
171
+ result
172
+ }