@grain/stdlib 0.4.4 → 0.5.0

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.
Files changed (97) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/LICENSE +1 -1
  3. package/array.gr +92 -73
  4. package/array.md +18 -18
  5. package/bigint.gr +497 -0
  6. package/bigint.md +811 -0
  7. package/buffer.gr +56 -217
  8. package/buffer.md +24 -17
  9. package/bytes.gr +103 -205
  10. package/bytes.md +19 -0
  11. package/char.gr +152 -166
  12. package/char.md +200 -0
  13. package/exception.md +6 -0
  14. package/float32.gr +159 -82
  15. package/float32.md +315 -0
  16. package/float64.gr +163 -82
  17. package/float64.md +315 -0
  18. package/hash.gr +53 -49
  19. package/int32.gr +479 -230
  20. package/int32.md +937 -0
  21. package/int64.gr +479 -230
  22. package/int64.md +937 -0
  23. package/list.gr +530 -116
  24. package/list.md +1141 -0
  25. package/map.gr +302 -121
  26. package/map.md +525 -0
  27. package/number.gr +51 -57
  28. package/number.md +37 -3
  29. package/option.gr +25 -25
  30. package/option.md +1 -1
  31. package/package.json +3 -3
  32. package/pervasives.gr +504 -52
  33. package/pervasives.md +1116 -0
  34. package/queue.gr +8 -1
  35. package/queue.md +10 -0
  36. package/random.gr +196 -0
  37. package/random.md +179 -0
  38. package/range.gr +26 -26
  39. package/regex.gr +1833 -842
  40. package/regex.md +11 -11
  41. package/result.md +1 -1
  42. package/runtime/bigint.gr +2045 -0
  43. package/runtime/bigint.md +326 -0
  44. package/runtime/dataStructures.gr +99 -279
  45. package/runtime/dataStructures.md +391 -0
  46. package/runtime/debug.gr +0 -1
  47. package/runtime/debug.md +6 -0
  48. package/runtime/equal.gr +40 -37
  49. package/runtime/equal.md +6 -0
  50. package/runtime/exception.gr +28 -15
  51. package/runtime/exception.md +30 -0
  52. package/runtime/gc.gr +50 -20
  53. package/runtime/gc.md +36 -0
  54. package/runtime/malloc.gr +32 -22
  55. package/runtime/malloc.md +55 -0
  56. package/runtime/numberUtils.gr +297 -142
  57. package/runtime/numberUtils.md +54 -0
  58. package/runtime/numbers.gr +1204 -453
  59. package/runtime/numbers.md +300 -0
  60. package/runtime/string.gr +193 -228
  61. package/runtime/string.md +24 -0
  62. package/runtime/stringUtils.gr +62 -38
  63. package/runtime/stringUtils.md +6 -0
  64. package/runtime/unsafe/constants.gr +17 -0
  65. package/runtime/unsafe/constants.md +72 -0
  66. package/runtime/unsafe/conv.gr +10 -10
  67. package/runtime/unsafe/conv.md +71 -0
  68. package/runtime/unsafe/errors.md +204 -0
  69. package/runtime/unsafe/memory.gr +14 -3
  70. package/runtime/unsafe/memory.md +54 -0
  71. package/runtime/unsafe/printWasm.gr +4 -4
  72. package/runtime/unsafe/printWasm.md +24 -0
  73. package/runtime/unsafe/tags.gr +11 -10
  74. package/runtime/unsafe/tags.md +120 -0
  75. package/runtime/unsafe/wasmf32.gr +9 -2
  76. package/runtime/unsafe/wasmf32.md +168 -0
  77. package/runtime/unsafe/wasmf64.gr +9 -2
  78. package/runtime/unsafe/wasmf64.md +168 -0
  79. package/runtime/unsafe/wasmi32.gr +65 -47
  80. package/runtime/unsafe/wasmi32.md +282 -0
  81. package/runtime/unsafe/wasmi64.gr +78 -50
  82. package/runtime/unsafe/wasmi64.md +300 -0
  83. package/runtime/utils/printing.gr +62 -0
  84. package/runtime/utils/printing.md +18 -0
  85. package/runtime/wasi.gr +200 -46
  86. package/runtime/wasi.md +839 -0
  87. package/set.gr +125 -121
  88. package/set.md +24 -21
  89. package/stack.gr +29 -29
  90. package/stack.md +4 -6
  91. package/string.gr +434 -415
  92. package/string.md +3 -3
  93. package/sys/file.gr +477 -482
  94. package/sys/process.gr +33 -47
  95. package/sys/random.gr +48 -20
  96. package/sys/random.md +38 -0
  97. package/sys/time.gr +12 -28
package/list.gr CHANGED
@@ -1,8 +1,31 @@
1
- // Standard library for list functionality
1
+ /**
2
+ * @module List: Utilities for working with lists.
3
+ *
4
+ * @example import List from "list"
5
+ *
6
+ * @since v0.2.0
7
+ * @history v0.1.0: Originally named `lists`
8
+ * @history v0.2.0: Renamed to `list`
9
+ */
2
10
 
3
- export *
11
+ /**
12
+ * @section Values: Functions for working with the List data type.
13
+ */
4
14
 
5
- let init = (length, fn) => {
15
+ /**
16
+ * Creates a new list of the specified length where each element is
17
+ * initialized with the result of an initializer function. The initializer
18
+ * is called with the index of each list element.
19
+ *
20
+ * @param length: The length of the new list
21
+ * @param fn: The initializer function to call with each index, where the value returned will be used to initialize the element
22
+ * @returns The new list
23
+ *
24
+ * @example List.init(5, n => n + 3) // [3, 4, 5, 6, 7]
25
+ *
26
+ * @since v0.3.0
27
+ */
28
+ export let init = (length, fn) => {
6
29
  // This method can be further improved by checking the length against a specific size
7
30
  // and determining if it can be made tail-recursive, then use List.reverse on it.
8
31
  // Which would be the same as OCaml does it in https://github.com/ocaml/ocaml/blob/03839754f46319aa36d9dad56940a6f3c3bcb48a/stdlib/list.ml#L79
@@ -16,204 +39,463 @@ let init = (length, fn) => {
16
39
  iter(0, length)
17
40
  }
18
41
 
19
- let length = (list) => {
42
+ /**
43
+ * Computes the length of the input list.
44
+ *
45
+ * @param list: The list to inspect
46
+ * @returns The number of elements in the list
47
+ *
48
+ * @since v0.1.0
49
+ * @history v0.2.0: Made the function tail-recursive
50
+ */
51
+ export let length = list => {
20
52
  let rec iter = (len, list) => {
21
53
  match (list) {
22
54
  [] => len,
23
- [_, ...rest] => iter(len + 1, rest)
24
- }
25
- }
26
- iter(0, list)
27
- }
28
-
29
- let sum = list => {
30
- let rec iter = (n, list) => {
31
- match (list) {
32
- [] => n,
33
- [first, ...rest] => iter(n + first, rest)
55
+ [_, ...rest] => iter(len + 1, rest),
34
56
  }
35
57
  }
36
58
  iter(0, list)
37
59
  }
38
60
 
39
- let reverse = (list) => {
61
+ /**
62
+ * Creates a new list with all elements in reverse order.
63
+ *
64
+ * @param list: The list to reverse
65
+ * @returns The new list
66
+ *
67
+ * @since v0.1.0
68
+ */
69
+ export let reverse = list => {
40
70
  let rec iter = (list, acc) => {
41
71
  match (list) {
42
72
  [] => acc,
43
- [first, ...rest] => iter(rest, [first, ...acc])
73
+ [first, ...rest] => iter(rest, [first, ...acc]),
44
74
  }
45
75
  }
46
76
  iter(list, [])
47
77
  }
48
78
 
49
- let rec append = (list1, list2) => {
79
+ /**
80
+ * Creates a new list with the elements of the first list followed by
81
+ * the elements of the second list.
82
+ *
83
+ * @param list1: The list containing elements to appear first
84
+ * @param list2: The list containing elements to appear second
85
+ * @returns The new list containing elements from `list1` followed by elements from `list2`
86
+ *
87
+ * @since v0.1.0
88
+ */
89
+ export let rec append = (list1, list2) => {
50
90
  match (list1) {
51
91
  [] => list2,
52
- [first, ...rest] => [first, ...append(rest, list2)]
92
+ [first, ...rest] => [first, ...append(rest, list2)],
53
93
  }
54
94
  }
55
95
 
56
- let rec contains = (value, list) => {
96
+ /**
97
+ * Checks if the value is an element of the input list.
98
+ * Uses the generic `==` structural equality operator.
99
+ *
100
+ * @param search: The value to compare
101
+ * @param list: The list to inspect
102
+ * @returns `true` if the value exists in the list or `false` otherwise
103
+ *
104
+ * @since v0.1.0
105
+ */
106
+ export let rec contains = (search, list) => {
57
107
  match (list) {
58
108
  [] => false,
59
- [first, ...rest] => (first == value) || contains(value, rest)
109
+ [first, ...rest] => first == search || contains(search, rest),
60
110
  }
61
111
  }
62
112
 
63
- let rec reduce = (fn, acc, list) => {
113
+ /**
114
+ * Combines all elements of a list using a reducer function,
115
+ * starting from the "head", or left side, of the list.
116
+ *
117
+ * In `List.reduce(fn, initial, list)`, `fn` is called with
118
+ * an accumulator and each element of the list, and returns
119
+ * a new accumulator. The final value is the last accumulator
120
+ * returned. The accumulator starts with value `initial`.
121
+ *
122
+ * @param fn: The reducer function to call on each element, where the value returned will be the next accumulator value
123
+ * @param initial: The initial value to use for the accumulator on the first iteration
124
+ * @param list: The list to iterate
125
+ * @returns The final accumulator returned from `fn`
126
+ *
127
+ * @example List.reduce((a, b) => a + b, 0, [1, 2, 3]) // 6
128
+ *
129
+ * @since v0.2.0
130
+ * @history v0.1.0: Originally named `foldLeft`
131
+ * @history v0.2.0: Renamed to `reduce`
132
+ */
133
+ export let rec reduce = (fn, initial, list) => {
64
134
  match (list) {
65
- [] => acc,
66
- [first, ...rest] => reduce(fn, fn(acc, first), rest)
135
+ [] => initial,
136
+ [first, ...rest] => reduce(fn, fn(initial, first), rest),
67
137
  }
68
138
  }
69
139
 
70
- let rec reduceRight = (fn, acc, list) => {
140
+ /**
141
+ * Combines all elements of a list using a reducer function,
142
+ * starting from the "end", or right side, of the list.
143
+ *
144
+ * In `List.reduceRight(fn, initial, list)`, `fn` is called with
145
+ * each element of the list and an accumulator, and returns
146
+ * a new accumulator. The final value is the last accumulator
147
+ * returned. The accumulator starts with value `initial`.
148
+ *
149
+ * @param fn: The reducer function to call on each element, where the value returned will be the next accumulator value
150
+ * @param initial: The initial value to use for the accumulator on the first iteration
151
+ * @param list: The list to iterate
152
+ * @returns The final accumulator returned from `fn`
153
+ *
154
+ * @example List.reduceRight((a, b) => b ++ a, "", ["baz", "bar", "foo"]) // "foobarbaz"
155
+ *
156
+ * @since v0.2.0
157
+ * @history v0.1.0: Originally named `foldRight`
158
+ * @history v0.2.0: Renamed to `reduceRight`
159
+ */
160
+ export let rec reduceRight = (fn, initial, list) => {
71
161
  match (list) {
72
- [] => acc,
73
- [first, ...rest] => fn(first, reduceRight(fn, acc, rest))
162
+ [] => initial,
163
+ [first, ...rest] => fn(first, reduceRight(fn, initial, rest)),
74
164
  }
75
165
  }
76
166
 
77
- let rec map = (fn, list) => {
167
+ /**
168
+ * Produces a new list initialized with the results of a mapper function
169
+ * called on each element of the input list.
170
+ *
171
+ * @param fn: The mapper function to call on each element, where the value returned will be used to initialize the element in the new list
172
+ * @param list: The list to iterate
173
+ * @returns The new list with mapped values
174
+ *
175
+ * @since v0.1.0
176
+ */
177
+ export let rec map = (fn, list) => {
78
178
  match (list) {
79
179
  [] => [],
80
- [first, ...rest] => [fn(first), ...map(fn, rest)]
180
+ [first, ...rest] => [fn(first), ...map(fn, rest)],
81
181
  }
82
182
  }
83
183
 
84
- let mapi = (fn, list) => {
184
+ /**
185
+ * Produces a new list initialized with the results of a mapper function
186
+ * called on each element of the input list and its index.
187
+ *
188
+ * @param fn: The mapper function to call on each element, where the value returned will be used to initialize the element in the new list
189
+ * @param list: The list to iterate
190
+ * @returns The new list with mapped values
191
+ *
192
+ * @since v0.1.0
193
+ */
194
+ export let mapi = (fn, list) => {
85
195
  let rec iter = (fn, list, index) => {
86
196
  match (list) {
87
197
  [] => [],
88
- [first, ...rest] => [fn(first, index), ...iter(fn, rest, index + 1)]
198
+ [first, ...rest] => [fn(first, index), ...iter(fn, rest, index + 1)],
89
199
  }
90
200
  }
91
201
  iter(fn, list, 0)
92
202
  }
93
203
 
94
- let rec flatMap = (fn, list) => {
204
+ /**
205
+ * Produces a new list by calling a function on each element
206
+ * of the input list. Each iteration produces an intermediate
207
+ * list, which are all appended to produce a "flattened" list
208
+ * of all results.
209
+ *
210
+ * @param fn: The function to be called on each element, where the value returned will be a list that gets appended to the new list
211
+ * @param list: The list to iterate
212
+ * @returns The new list
213
+ *
214
+ * @since v0.2.0
215
+ */
216
+ export let rec flatMap = (fn, list) => {
95
217
  match (list) {
96
218
  [] => [],
97
- [first, ...rest] => append(fn(first), flatMap(fn, rest))
219
+ [first, ...rest] => append(fn(first), flatMap(fn, rest)),
98
220
  }
99
221
  }
100
222
 
101
- let every = (fn, list) => {
102
- reduce((acc, value) => { acc && fn(value) }, true, list)
223
+ /**
224
+ * Checks that the given condition is satisfied for all
225
+ * elements in the input list.
226
+ *
227
+ * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
228
+ * @param list: The list to check
229
+ * @returns `true` if all elements satify the condition or `false` otherwise
230
+ *
231
+ * @since v0.1.0
232
+ */
233
+ export let every = (fn, list) => {
234
+ reduce((acc, value) => {
235
+ acc && fn(value)
236
+ }, true, list)
103
237
  }
104
238
 
105
- let some = (fn, list) => {
106
- reduce((acc, value) => { acc || fn(value) }, false, list)
239
+ /**
240
+ * Checks that the given condition is satisfied **at least
241
+ * once** by an element in the input list.
242
+ *
243
+ * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
244
+ * @param list: The list to iterate
245
+ * @returns `true` if one or more elements satify the condition or `false` otherwise
246
+ *
247
+ * @since v0.1.0
248
+ */
249
+ export let some = (fn, list) => {
250
+ reduce((acc, value) => {
251
+ acc || fn(value)
252
+ }, false, list)
107
253
  }
108
254
 
109
- let rec forEach = (fn, list) => {
255
+ /**
256
+ * Iterates a list, calling an iterator function on each element.
257
+ *
258
+ * @param fn: The iterator function to call with each element
259
+ * @param list: The list to iterate
260
+ *
261
+ * @since v0.1.0
262
+ */
263
+ export let rec forEach = (fn, list) => {
110
264
  match (list) {
111
265
  [] => void,
112
266
  [first, ...rest] => {
113
- fn(first)
267
+ fn(first): Void
114
268
  forEach(fn, rest)
115
- }
269
+ },
116
270
  }
117
271
  }
118
272
 
119
- let forEachi = (fn, list) => {
273
+ /**
274
+ * Iterates a list, calling an iterator function on each element.
275
+ * Also passes the index as the second argument to the function.
276
+ *
277
+ * @param fn: The iterator function to call with each element
278
+ * @param list: The list to iterate
279
+ *
280
+ * @since v0.1.0
281
+ */
282
+ export let forEachi = (fn, list) => {
120
283
  let rec iter = (fn, list, index) => {
121
284
  match (list) {
122
285
  [] => void,
123
286
  [first, ...rest] => {
124
- fn(first, index)
287
+ fn(first, index): Void
125
288
  iter(fn, rest, index + 1)
126
- }
289
+ },
127
290
  }
128
291
  }
129
292
  iter(fn, list, 0)
130
293
  }
131
294
 
132
- let rec filter = (fn, list) => {
295
+ /**
296
+ * Produces a new list by calling a function on each element of
297
+ * the input list and only including it in the result list if the element satisfies
298
+ * the condition.
299
+ *
300
+ * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
301
+ * @param list: The list to iterate
302
+ * @returns The new list containing elements where `fn` returned `true`
303
+ *
304
+ * @since v0.1.0
305
+ */
306
+ export let rec filter = (fn, list) => {
133
307
  match (list) {
134
308
  [] => [],
135
- [first, ...rest] => if (fn(first)) [first, ...filter(fn, rest)] else filter(fn, rest)
309
+ [first, ...rest] =>
310
+ if (fn(first)) [first, ...filter(fn, rest)] else filter(fn, rest),
136
311
  }
137
312
  }
138
313
 
139
- let filteri = (fn, list) => {
314
+ /**
315
+ * Produces a new list by calling a function on each element of
316
+ * the input list and only including it in the result list if the element satisfies
317
+ * the condition. Also passes the index to the function.
318
+ *
319
+ * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
320
+ * @param list: The list to iterate
321
+ * @returns The new list containing elements where `fn` returned `true`
322
+ *
323
+ * @since v0.3.0
324
+ */
325
+ export let filteri = (fn, list) => {
140
326
  let rec iter = (fn, list, index) => {
141
327
  match (list) {
142
328
  [] => [],
143
- [first, ...rest] => if (fn(first, index)) [first, ...iter(fn, rest, index + 1)] else iter(fn, rest, index + 1)
329
+ [first, ...rest] =>
330
+ if (fn(first, index)) [first, ...iter(fn, rest, index + 1)]
331
+ else iter(fn, rest, index + 1),
144
332
  }
145
333
  }
146
334
  iter(fn, list, 0)
147
335
  }
148
336
 
149
- let rec reject = (fn, list) => {
337
+ /**
338
+ * Produces a new list by calling a function on each element of
339
+ * the input list and excluding it from the result list if the element satisfies
340
+ * the condition.
341
+ *
342
+ * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
343
+ * @param list: The list to iterate
344
+ * @returns The new list containing elements where `fn` returned `false`
345
+ *
346
+ * @since v0.1.0
347
+ */
348
+ export let rec reject = (fn, list) => {
150
349
  match (list) {
151
350
  [] => [],
152
- [first, ...rest] => if (!fn(first)) [first, ...reject(fn, rest)] else reject(fn, rest)
351
+ [first, ...rest] =>
352
+ if (!fn(first)) [first, ...reject(fn, rest)] else reject(fn, rest),
153
353
  }
154
354
  }
155
355
 
156
- let head = (list) => {
356
+ /**
357
+ * Provides `Some(element)` containing the first element, or "head", of
358
+ * the input list or `None` if the list is empty.
359
+ *
360
+ * @param list: The list to access
361
+ * @returns `Some(firstElement)` if the list has elements or `None` otherwise
362
+ *
363
+ * @since v0.2.0
364
+ * @history v0.1.0: Originally named `hd`
365
+ * @history v0.2.0: Renamed to `head`
366
+ * @history v0.3.0: Return type converted to `Option` type
367
+ */
368
+ export let head = list => {
157
369
  match (list) {
158
370
  [] => None,
159
- [first, ..._] => Some(first)
371
+ [first, ..._] => Some(first),
160
372
  }
161
373
  }
162
374
 
163
- let tail = (list) => {
375
+ /**
376
+ * Provides `Some(tail)` containing all list items except the first element, or "tail", of
377
+ * the input list or `None` if the list is empty.
378
+ *
379
+ * @param list: The list to access
380
+ * @returns `Some(tail)` if the list has elements or `None` otherwise
381
+ *
382
+ * @since v0.2.0
383
+ * @history v0.1.0: Originally named `tl`
384
+ * @history v0.2.0: Renamed to `tail`
385
+ * @history v0.3.0: Return type converted to `Option` type
386
+ */
387
+ export let tail = list => {
164
388
  match (list) {
165
389
  [] => None,
166
- [_, ...rest] => Some(rest)
390
+ [_, ...rest] => Some(rest),
167
391
  }
168
392
  }
169
393
 
170
- let rec nth = (index, list) => {
394
+ /**
395
+ * Provides `Some(element)` containing the element in the list at the specified index
396
+ * or `None` if the index is out-of-bounds or the list is empty.
397
+ *
398
+ * @param index: The index to access
399
+ * @param list: The list to access
400
+ * @returns `Some(element)` if the list contains an element at the index or `None` otherwise
401
+ *
402
+ * @since v0.1.0
403
+ * @history v0.1.0: Originally failed for index out-of-bounds or list empty
404
+ * @history v0.3.0: Return type converted to `Option` type
405
+ */
406
+ export let rec nth = (index, list) => {
171
407
  if (index < 0) {
172
408
  None
173
409
  } else {
174
410
  match (list) {
175
411
  [] => None,
176
- [first, ...rest] => if (index == 0) Some(first) else nth(index - 1, rest)
412
+ [first, ...rest] => if (index == 0) Some(first) else nth(index - 1, rest),
177
413
  }
178
414
  }
179
415
  }
180
416
 
181
- let rec flatten = (list) => {
417
+ /**
418
+ * Flattens nested lists.
419
+ *
420
+ * @param list: The list to flatten
421
+ * @returns A new list containing all nested list elements combined
422
+ *
423
+ * @example List.flatten([[1, 2], [3, 4]]) // [1, 2, 3, 4]
424
+ *
425
+ * @since v0.1.0
426
+ */
427
+ export let rec flatten = list => {
182
428
  match (list) {
183
429
  [] => [],
184
- [first, ...rest] => append(first, flatten(rest))
430
+ [first, ...rest] => append(first, flatten(rest)),
185
431
  }
186
432
  }
187
433
 
188
- let rec insert = (value, index, list) => {
434
+ /**
435
+ * Inserts a new value into a list at the specified index.
436
+ * Fails if the index is out-of-bounds.
437
+ *
438
+ * @param value: The value to insert
439
+ * @param index: The index to update
440
+ * @param list: The list to update
441
+ * @returns The new list
442
+ *
443
+ * @since v0.1.0
444
+ */
445
+ export let rec insert = (value, index, list) => {
189
446
  if (index < 0) {
190
447
  fail "insert index cannot be a negative number"
191
448
  } else {
192
- match(list) {
449
+ match (list) {
193
450
  [] => if (index == 0) [value] else fail "insert index is out-of-bounds",
194
- [first, ...rest] => if (index == 0) [value, ...list] else [first, ...insert(value, index - 1, rest)]
451
+ [first, ...rest] =>
452
+ if (index == 0) [value, ...list]
453
+ else [first, ...insert(value, index - 1, rest)],
195
454
  }
196
455
  }
197
456
  }
198
457
 
199
- let count = (fn, list) => {
458
+ /**
459
+ * Counts the number of elements in a list that satisfy the given condition.
460
+ *
461
+ * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
462
+ * @param list: The list to iterate
463
+ * @returns The total number of elements that satisfy the condition
464
+ *
465
+ * @since v0.1.0
466
+ * @history v0.2.0: Made the function tail-recursive
467
+ */
468
+ export let count = (fn, list) => {
200
469
  let rec iter = (n, list) => {
201
470
  match (list) {
202
471
  [] => n,
203
- [first, ...rest] => if (fn(first)) iter(n + 1, rest) else iter(n, rest)
472
+ [first, ...rest] => if (fn(first)) iter(n + 1, rest) else iter(n, rest),
204
473
  }
205
474
  }
206
475
  iter(0, list)
207
476
  }
208
477
 
209
- let part = (count, list) => {
478
+ /**
479
+ * Split a list into two, with the first list containing the required number of elements.
480
+ * Fails if the input list doesn't contain at least the required amount of elements.
481
+ *
482
+ * @param count: The number of elements required
483
+ * @param list: The list to split
484
+ * @returns Two lists where the first contains exactly the required amount of elements and the second contains any remaining elements
485
+ *
486
+ * @since v0.1.0
487
+ */
488
+ export let part = (count, list) => {
210
489
  if (count < 0) {
211
490
  fail "part count cannot be a negative number"
212
491
  } else {
213
492
  let rec iter = (list1, list2, count) => {
214
493
  match (list2) {
215
- [] => if (count > 0) fail "part count is out-of-bounds" else (list1, list2),
216
- [first, ...rest] => if (count > 0) iter([first, ...list1], rest, count - 1) else (list1, list2)
494
+ [] =>
495
+ if (count > 0) fail "part count is out-of-bounds" else (list1, list2),
496
+ [first, ...rest] =>
497
+ if (count > 0) iter([first, ...list1], rest, count - 1)
498
+ else (list1, list2),
217
499
  }
218
500
  }
219
501
  let (pt1, pt2) = iter([], list, count)
@@ -221,110 +503,238 @@ let part = (count, list) => {
221
503
  }
222
504
  }
223
505
 
224
- let rotate = (count, list) => {
225
- let (beginning, end) = if (count >= 0) part(count, list) else part(length(list) + count, list)
506
+ /**
507
+ * Rotates list elements by the specified amount to the left.
508
+ *
509
+ * If value is negative, list elements will be rotated by the
510
+ * specified amount to the right. See examples.
511
+ *
512
+ * Fails if the input list doesn't contain at least the required amount of elements.
513
+ *
514
+ * @param count: The number of elements to rotate by
515
+ * @param list: The list to be rotated
516
+ *
517
+ * @example List.rotate(2, [1, 2, 3, 4, 5]) // [3, 4, 5, 1, 2]
518
+ * @example List.rotate(-1, [1, 2, 3, 4, 5]) // [5, 1, 2, 3, 4]
519
+ *
520
+ * @since v0.1.0
521
+ */
522
+ export let rotate = (count, list) => {
523
+ let (beginning, end) = if (count >= 0) part(count, list)
524
+ else part(length(list) + count, list)
226
525
  append(end, beginning)
227
526
  }
228
527
 
229
- let unique = (list) => {
528
+ /**
529
+ * Produces a new list with any duplicates removed.
530
+ * Uses the generic `==` structural equality operator.
531
+ *
532
+ * @param list: The list to filter
533
+ * @returns The new list with only unique values
534
+ *
535
+ * @since v0.2.0
536
+ * @history v0.1.0: Originally named `uniq`
537
+ * @history v0.2.0: Renamed to `unique`
538
+ */
539
+ export let unique = list => {
230
540
  let rec iter = (list, acc) => {
231
541
  match (list) {
232
542
  [] => reverse(acc),
233
- [first, ...rest] => if (contains(first, acc)) iter(rest, acc) else iter(rest, [first, ...acc])
543
+ [first, ...rest] =>
544
+ if (contains(first, acc)) iter(rest, acc)
545
+ else iter(rest, [first, ...acc]),
234
546
  }
235
547
  }
236
548
  iter(list, [])
237
549
  }
238
550
 
239
- let rec drop = (n, list) => {
240
- if (n < 0) {
551
+ /**
552
+ * Produces a new list with the specified number of elements removed from
553
+ * the beginning of the input list.
554
+ *
555
+ * Fails if the specified amount is a negative number.
556
+ *
557
+ * @param count: The amount of elements to remove
558
+ * @param list: The input list
559
+ * @returns The new list without the dropped elements
560
+ *
561
+ * @since v0.2.0
562
+ */
563
+ export let rec drop = (count, list) => {
564
+ if (count < 0) {
241
565
  fail "number of items to drop cannot be a negative number"
242
566
  } else {
243
- match ((n, list)) {
244
- (_,[]) => [],
567
+ match ((count, list)) {
568
+ (_, []) => [],
245
569
  (n, _) when n == 0 => list,
246
- (n, [first, ...rest]) => drop(n - 1, rest)
570
+ (n, [first, ...rest]) => drop(n - 1, rest),
247
571
  }
248
572
  }
249
573
  }
250
574
 
251
- let rec dropWhile = (fn, list) => {
575
+ /**
576
+ * Produces a new list with the elements removed from the beginning
577
+ * of the input list until they no longer satisfy the given condition.
578
+ * Stops when the predicate function returns `false`.
579
+ *
580
+ * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
581
+ * @param list: The input list
582
+ * @returns The new list without the dropped elements
583
+ *
584
+ * @since v0.2.0
585
+ */
586
+ export let rec dropWhile = (fn, list) => {
252
587
  match (list) {
253
588
  [] => list,
254
- [first, ...rest] => if (fn(first)) dropWhile(fn, rest) else list
589
+ [first, ...rest] => if (fn(first)) dropWhile(fn, rest) else list,
255
590
  }
256
591
  }
257
592
 
258
- let rec take = (n, list) => {
259
- if (n < 0) {
593
+ /**
594
+ * Produces a new list with–at most—the specified amount elements from
595
+ * the beginning of the input list.
596
+ *
597
+ * Fails if the specified amount is a negative number.
598
+ *
599
+ * @param count: The amount of elements to keep
600
+ * @param list: The input list
601
+ * @returns The new list containing the taken elements
602
+ *
603
+ * @since v0.2.0
604
+ */
605
+ export let rec take = (count, list) => {
606
+ if (count < 0) {
260
607
  fail "number of items to take cannot be a negative number"
261
608
  } else {
262
- match ((n, list)) {
609
+ match ((count, list)) {
263
610
  (_, []) => list,
264
611
  (n, _) when n == 0 => [],
265
- (n, [first, ...rest]) => [first, ...take(n-1, rest)]
612
+ (n, [first, ...rest]) => [first, ...take(n - 1, rest)],
266
613
  }
267
614
  }
268
615
  }
269
616
 
270
- let rec takeWhile = (p, list) => {
617
+ /**
618
+ * Produces a new list with elements from the beginning of the input list
619
+ * as long as they satisfy the given condition.
620
+ * Stops when the predicate function returns `false`.
621
+ *
622
+ * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
623
+ * @param list: The input list
624
+ * @returns The new list containing the taken elements
625
+ *
626
+ * @since v0.2.0
627
+ */
628
+ export let rec takeWhile = (fn, list) => {
271
629
  match (list) {
272
630
  [] => [],
273
- [first, ...rest] => if (p(first)) [first, ...takeWhile(p, rest)] else []
631
+ [first, ...rest] => if (fn(first)) [first, ...takeWhile(fn, rest)] else [],
274
632
  }
275
633
  }
276
634
 
277
- let rec find = (fn, list) => {
635
+ /**
636
+ * Finds the first element in a list that satifies the given condition.
637
+ *
638
+ * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
639
+ * @param list: The list to search
640
+ * @returns `Some(element)` containing the first value found or `None` otherwise
641
+ *
642
+ * @since v0.2.0
643
+ * @history v0.2.0: Originally failed if the list was empty
644
+ * @history v0.3.0: Return type converted to `Option` type
645
+ */
646
+ export let rec find = (fn, list) => {
278
647
  match (list) {
279
648
  [] => None,
280
- [first, ...rest] => if (fn(first)) Some(first) else find(fn, rest)
649
+ [first, ...rest] => if (fn(first)) Some(first) else find(fn, rest),
281
650
  }
282
651
  }
283
652
 
284
- let findIndex = (fn, list) => {
653
+ /**
654
+ * Finds the first index in a list where the element satifies the given condition.
655
+ *
656
+ * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
657
+ * @param list: The list to search
658
+ * @returns `Some(index)` containing the index of the first element found or `None` otherwise
659
+ *
660
+ * @since v0.2.0
661
+ * @history v0.2.0: Originally failed if the list was empty
662
+ * @history v0.3.0: Return type converted to `Option` type
663
+ */
664
+ export let findIndex = (fn, list) => {
285
665
  let rec findItemIndex = (l, index) => {
286
666
  match (l) {
287
667
  [] => None,
288
- [first, ...rest] => if (fn(first)) Some(index) else findItemIndex(rest, index + 1)
668
+ [first, ...rest] =>
669
+ if (fn(first)) Some(index) else findItemIndex(rest, index + 1),
289
670
  }
290
671
  }
291
672
  findItemIndex(list, 0)
292
673
  }
293
674
 
294
- let product = (a, b) => {
675
+ /**
676
+ * Combines two lists into a Cartesian product of tuples containing
677
+ * all ordered pairs `(a, b)`.
678
+ *
679
+ * @param list1: The list to provide values for the first tuple element
680
+ * @param list2: The list to provide values for the second tuple element
681
+ * @returns The new list containing all pairs of `(a, b)`
682
+ *
683
+ * @since v0.2.0
684
+ */
685
+ export let product = (list1, list2) => {
295
686
  let mut list = []
296
- forEach((aItem) => {
297
- forEach((bItem) => {
298
- list = [(aItem, bItem), ...list]
299
- },b)
300
- }, a)
687
+ forEach(aItem => {
688
+ forEach(bItem => {
689
+ list = [(aItem, bItem), ...list]
690
+ }, list2)
691
+ }, list1)
301
692
  reverse(list)
302
693
  }
303
694
 
304
- let sub = (start, length, list) => {
695
+ /**
696
+ * Provides the subset of a list given zero-based start index and amount of elements
697
+ * to include.
698
+ *
699
+ * Fails if the start index or amount of elements are negative numbers.
700
+ *
701
+ * @param start: The index of the list where the subset will begin (inclusive)
702
+ * @param length: The amount of elements to be included in the subset
703
+ * @param list: The input list
704
+ * @returns The subset of the list
705
+ *
706
+ * @since v0.2.0
707
+ */
708
+ export let sub = (start, length, list) => {
305
709
  take(length, drop(start, list))
306
710
  }
307
711
 
308
- // Join the given list of strings with the given separator
309
- // @param separator: String - The separator to insert between items in the string
310
- // @param items: List<String> - The input strings
311
- // @returns String
312
- let join = (separator: String, items: List<String>) => {
712
+ /**
713
+ * Combine the given list of strings into one string with the specified
714
+ * separator inserted between each item.
715
+ *
716
+ * @param separator: The separator to insert between elements
717
+ * @param list: The list to combine
718
+ * @returns The combined elements with the separator between each
719
+ *
720
+ * @since v0.4.0
721
+ */
722
+ export let join = (separator: String, list: List<String>) => {
313
723
  let rec iter = (sep, acc, rem) => {
314
- match(rem) {
724
+ match (rem) {
315
725
  [] => acc,
316
726
  [hd, ...tl] => {
317
- let newAcc = match(acc) {
727
+ let newAcc = match (acc) {
318
728
  None => Some(hd),
319
- Some(s) => Some(hd ++ sep ++ s)
729
+ Some(s) => Some(hd ++ sep ++ s),
320
730
  }
321
731
  iter(sep, newAcc, tl)
322
- }
732
+ },
323
733
  }
324
734
  }
325
735
 
326
736
  // Reverse and reduce to take advantage of TCE
327
- match(iter(separator, None, reverse(items))) {
737
+ match (iter(separator, None, reverse(list))) {
328
738
  None => "",
329
739
  Some(s) => s,
330
740
  }
@@ -332,29 +742,33 @@ let join = (separator: String, items: List<String>) => {
332
742
 
333
743
  /**
334
744
  * Reverses the first list and appends the second list to the end.
335
- *
745
+ *
336
746
  * @param list1: The list to reverse
337
747
  * @param list2: The list to append
748
+ * @returns The new list
749
+ *
338
750
  * @since v0.4.5
339
751
  */
340
- let rec revAppend = (list1, list2) => {
752
+ export let rec revAppend = (list1, list2) => {
341
753
  match (list1) {
342
754
  [hd, ...tl] => revAppend(tl, [hd, ...list2]),
343
- [] => list2
755
+ [] => list2,
344
756
  }
345
757
  }
346
758
 
347
759
  /**
348
760
  * Sorts the given list based on a given comparator function. The resulting list is sorted in increasing order.
349
- *
761
+ *
350
762
  * Ordering is calculated using a comparator function which takes two list elements and must return 0 if both are equal, a positive number if the first is greater, and a negative number if the first is smaller.
351
763
  * @param comp: The comparator function used to indicate sort order
352
764
  * @param list: The list to be sorted
765
+ * @returns The sorted list
766
+ *
353
767
  * @since v0.4.5
354
768
  */
355
- let sort = (comp, list) => {
769
+ export let sort = (comp, list) => {
356
770
  let rec merge = (left, right, list) => {
357
- match((left, right)){
771
+ match ((left, right)) {
358
772
  (_, []) => {
359
773
  revAppend(list, left)
360
774
  },
@@ -362,19 +776,19 @@ let sort = (comp, list) => {
362
776
  revAppend(list, right)
363
777
  },
364
778
  ([lhd, ...ltl], [rhd, ...rtl]) => {
365
- if(comp(lhd, rhd) < 0){
366
- merge(ltl, right, append([lhd],list))
367
- }else{
779
+ if (comp(lhd, rhd) < 0) {
780
+ merge(ltl, right, append([lhd], list))
781
+ } else {
368
782
  merge(left, rtl, append([rhd], list))
369
783
  }
370
- }
784
+ },
371
785
  }
372
786
  }
373
787
 
374
- let rec mergesort = (list) => {
375
- if(length(list) <= 1){
788
+ let rec mergesort = list => {
789
+ if (length(list) <= 1) {
376
790
  list
377
- }else{
791
+ } else {
378
792
  let middle = length(list) / 2
379
793
  let (left, right) = part(middle, list)
380
794
  merge(mergesort(left), mergesort(right), [])