@grain/stdlib 0.4.6 → 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 (82) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/array.gr +18 -18
  3. package/array.md +18 -18
  4. package/bigint.gr +497 -0
  5. package/bigint.md +811 -0
  6. package/buffer.gr +49 -213
  7. package/buffer.md +24 -17
  8. package/bytes.gr +100 -202
  9. package/bytes.md +19 -0
  10. package/char.gr +63 -133
  11. package/exception.md +6 -0
  12. package/float32.gr +39 -78
  13. package/float64.gr +43 -78
  14. package/hash.gr +37 -37
  15. package/int32.gr +152 -198
  16. package/int32.md +104 -0
  17. package/int64.gr +151 -197
  18. package/int64.md +104 -0
  19. package/list.gr +467 -70
  20. package/list.md +1141 -0
  21. package/map.gr +192 -7
  22. package/map.md +525 -0
  23. package/number.gr +30 -54
  24. package/number.md +3 -3
  25. package/option.md +1 -1
  26. package/package.json +3 -3
  27. package/pervasives.gr +499 -59
  28. package/pervasives.md +1116 -0
  29. package/queue.gr +4 -0
  30. package/queue.md +10 -0
  31. package/random.gr +196 -0
  32. package/random.md +179 -0
  33. package/regex.gr +1833 -842
  34. package/regex.md +11 -11
  35. package/result.md +1 -1
  36. package/runtime/bigint.gr +2045 -0
  37. package/runtime/bigint.md +326 -0
  38. package/runtime/dataStructures.gr +99 -278
  39. package/runtime/dataStructures.md +391 -0
  40. package/runtime/debug.md +6 -0
  41. package/runtime/equal.gr +5 -23
  42. package/runtime/equal.md +6 -0
  43. package/runtime/exception.md +30 -0
  44. package/runtime/gc.gr +20 -3
  45. package/runtime/gc.md +36 -0
  46. package/runtime/malloc.gr +13 -11
  47. package/runtime/malloc.md +55 -0
  48. package/runtime/numberUtils.gr +91 -41
  49. package/runtime/numberUtils.md +54 -0
  50. package/runtime/numbers.gr +1043 -391
  51. package/runtime/numbers.md +300 -0
  52. package/runtime/string.gr +136 -230
  53. package/runtime/string.md +24 -0
  54. package/runtime/stringUtils.gr +58 -38
  55. package/runtime/stringUtils.md +6 -0
  56. package/runtime/unsafe/constants.gr +17 -0
  57. package/runtime/unsafe/constants.md +72 -0
  58. package/runtime/unsafe/conv.md +71 -0
  59. package/runtime/unsafe/errors.md +204 -0
  60. package/runtime/unsafe/memory.md +54 -0
  61. package/runtime/unsafe/printWasm.md +24 -0
  62. package/runtime/unsafe/tags.gr +9 -8
  63. package/runtime/unsafe/tags.md +120 -0
  64. package/runtime/unsafe/wasmf32.md +168 -0
  65. package/runtime/unsafe/wasmf64.md +168 -0
  66. package/runtime/unsafe/wasmi32.md +282 -0
  67. package/runtime/unsafe/wasmi64.md +300 -0
  68. package/runtime/utils/printing.gr +62 -0
  69. package/runtime/utils/printing.md +18 -0
  70. package/runtime/wasi.gr +1 -1
  71. package/runtime/wasi.md +839 -0
  72. package/set.gr +17 -8
  73. package/set.md +24 -21
  74. package/stack.gr +3 -3
  75. package/stack.md +4 -6
  76. package/string.gr +194 -329
  77. package/string.md +3 -3
  78. package/sys/file.gr +245 -429
  79. package/sys/process.gr +27 -45
  80. package/sys/random.gr +47 -16
  81. package/sys/random.md +38 -0
  82. package/sys/time.gr +11 -27
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,7 +39,16 @@ 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,
@@ -26,17 +58,15 @@ let length = list => {
26
58
  iter(0, list)
27
59
  }
28
60
 
29
- let sum = list => {
30
- let rec iter = (n, list) => {
31
- match (list) {
32
- [] => n,
33
- [first, ...rest] => iter(n + first, rest),
34
- }
35
- }
36
- iter(0, list)
37
- }
38
-
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,
@@ -46,42 +76,122 @@ let reverse = list => {
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
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
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
  [] => [],
@@ -91,41 +201,90 @@ let mapi = (fn, list) => {
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
219
  [first, ...rest] => append(fn(first), flatMap(fn, rest)),
98
220
  }
99
221
  }
100
222
 
101
- let every = (fn, 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) => {
102
234
  reduce((acc, value) => {
103
235
  acc && fn(value)
104
236
  }, true, list)
105
237
  }
106
238
 
107
- let some = (fn, 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) => {
108
250
  reduce((acc, value) => {
109
251
  acc || fn(value)
110
252
  }, false, list)
111
253
  }
112
254
 
113
- 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) => {
114
264
  match (list) {
115
265
  [] => void,
116
266
  [first, ...rest] => {
117
- fn(first)
267
+ fn(first): Void
118
268
  forEach(fn, rest)
119
269
  },
120
270
  }
121
271
  }
122
272
 
123
- 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) => {
124
283
  let rec iter = (fn, list, index) => {
125
284
  match (list) {
126
285
  [] => void,
127
286
  [first, ...rest] => {
128
- fn(first, index)
287
+ fn(first, index): Void
129
288
  iter(fn, rest, index + 1)
130
289
  },
131
290
  }
@@ -133,7 +292,18 @@ let forEachi = (fn, list) => {
133
292
  iter(fn, list, 0)
134
293
  }
135
294
 
136
- 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) => {
137
307
  match (list) {
138
308
  [] => [],
139
309
  [first, ...rest] =>
@@ -141,7 +311,18 @@ let rec filter = (fn, list) => {
141
311
  }
142
312
  }
143
313
 
144
- 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) => {
145
326
  let rec iter = (fn, list, index) => {
146
327
  match (list) {
147
328
  [] => [],
@@ -153,7 +334,18 @@ let filteri = (fn, list) => {
153
334
  iter(fn, list, 0)
154
335
  }
155
336
 
156
- 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) => {
157
349
  match (list) {
158
350
  [] => [],
159
351
  [first, ...rest] =>
@@ -161,21 +353,57 @@ let rec reject = (fn, list) => {
161
353
  }
162
354
  }
163
355
 
164
- 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 => {
165
369
  match (list) {
166
370
  [] => None,
167
371
  [first, ..._] => Some(first),
168
372
  }
169
373
  }
170
374
 
171
- 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 => {
172
388
  match (list) {
173
389
  [] => None,
174
390
  [_, ...rest] => Some(rest),
175
391
  }
176
392
  }
177
393
 
178
- 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) => {
179
407
  if (index < 0) {
180
408
  None
181
409
  } else {
@@ -186,14 +414,35 @@ let rec nth = (index, list) => {
186
414
  }
187
415
  }
188
416
 
189
- 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 => {
190
428
  match (list) {
191
429
  [] => [],
192
430
  [first, ...rest] => append(first, flatten(rest)),
193
431
  }
194
432
  }
195
433
 
196
- 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) => {
197
446
  if (index < 0) {
198
447
  fail "insert index cannot be a negative number"
199
448
  } else {
@@ -206,7 +455,17 @@ let rec insert = (value, index, list) => {
206
455
  }
207
456
  }
208
457
 
209
- 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) => {
210
469
  let rec iter = (n, list) => {
211
470
  match (list) {
212
471
  [] => n,
@@ -216,7 +475,17 @@ let count = (fn, list) => {
216
475
  iter(0, list)
217
476
  }
218
477
 
219
- 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) => {
220
489
  if (count < 0) {
221
490
  fail "part count cannot be a negative number"
222
491
  } else {
@@ -234,13 +503,40 @@ let part = (count, list) => {
234
503
  }
235
504
  }
236
505
 
237
- let rotate = (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) => {
238
523
  let (beginning, end) = if (count >= 0) part(count, list)
239
524
  else part(length(list) + count, list)
240
525
  append(end, beginning)
241
526
  }
242
527
 
243
- 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 => {
244
540
  let rec iter = (list, acc) => {
245
541
  match (list) {
246
542
  [] => reverse(acc),
@@ -252,11 +548,23 @@ let unique = list => {
252
548
  iter(list, [])
253
549
  }
254
550
 
255
- let rec drop = (n, list) => {
256
- 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) {
257
565
  fail "number of items to drop cannot be a negative number"
258
566
  } else {
259
- match ((n, list)) {
567
+ match ((count, list)) {
260
568
  (_, []) => [],
261
569
  (n, _) when n == 0 => list,
262
570
  (n, [first, ...rest]) => drop(n - 1, rest),
@@ -264,18 +572,41 @@ let rec drop = (n, list) => {
264
572
  }
265
573
  }
266
574
 
267
- 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) => {
268
587
  match (list) {
269
588
  [] => list,
270
589
  [first, ...rest] => if (fn(first)) dropWhile(fn, rest) else list,
271
590
  }
272
591
  }
273
592
 
274
- let rec take = (n, list) => {
275
- 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) {
276
607
  fail "number of items to take cannot be a negative number"
277
608
  } else {
278
- match ((n, list)) {
609
+ match ((count, list)) {
279
610
  (_, []) => list,
280
611
  (n, _) when n == 0 => [],
281
612
  (n, [first, ...rest]) => [first, ...take(n - 1, rest)],
@@ -283,21 +614,54 @@ let rec take = (n, list) => {
283
614
  }
284
615
  }
285
616
 
286
- 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) => {
287
629
  match (list) {
288
630
  [] => [],
289
- [first, ...rest] => if (p(first)) [first, ...takeWhile(p, rest)] else [],
631
+ [first, ...rest] => if (fn(first)) [first, ...takeWhile(fn, rest)] else [],
290
632
  }
291
633
  }
292
634
 
293
- 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) => {
294
647
  match (list) {
295
648
  [] => None,
296
649
  [first, ...rest] => if (fn(first)) Some(first) else find(fn, rest),
297
650
  }
298
651
  }
299
652
 
300
- 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) => {
301
665
  let rec findItemIndex = (l, index) => {
302
666
  match (l) {
303
667
  [] => None,
@@ -308,25 +672,54 @@ let findIndex = (fn, list) => {
308
672
  findItemIndex(list, 0)
309
673
  }
310
674
 
311
- 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) => {
312
686
  let mut list = []
313
687
  forEach(aItem => {
314
688
  forEach(bItem => {
315
689
  list = [(aItem, bItem), ...list]
316
- }, b)
317
- }, a)
690
+ }, list2)
691
+ }, list1)
318
692
  reverse(list)
319
693
  }
320
694
 
321
- 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) => {
322
709
  take(length, drop(start, list))
323
710
  }
324
711
 
325
- // Join the given list of strings with the given separator
326
- // @param separator: String - The separator to insert between items in the string
327
- // @param items: List<String> - The input strings
328
- // @returns String
329
- 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>) => {
330
723
  let rec iter = (sep, acc, rem) => {
331
724
  match (rem) {
332
725
  [] => acc,
@@ -341,7 +734,7 @@ let join = (separator: String, items: List<String>) => {
341
734
  }
342
735
 
343
736
  // Reverse and reduce to take advantage of TCE
344
- match (iter(separator, None, reverse(items))) {
737
+ match (iter(separator, None, reverse(list))) {
345
738
  None => "",
346
739
  Some(s) => s,
347
740
  }
@@ -349,12 +742,14 @@ let join = (separator: String, items: List<String>) => {
349
742
 
350
743
  /**
351
744
  * Reverses the first list and appends the second list to the end.
352
- *
745
+ *
353
746
  * @param list1: The list to reverse
354
747
  * @param list2: The list to append
748
+ * @returns The new list
749
+ *
355
750
  * @since v0.4.5
356
751
  */
357
- let rec revAppend = (list1, list2) => {
752
+ export let rec revAppend = (list1, list2) => {
358
753
  match (list1) {
359
754
  [hd, ...tl] => revAppend(tl, [hd, ...list2]),
360
755
  [] => list2,
@@ -363,13 +758,15 @@ let rec revAppend = (list1, list2) => {
363
758
 
364
759
  /**
365
760
  * Sorts the given list based on a given comparator function. The resulting list is sorted in increasing order.
366
- *
761
+ *
367
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.
368
763
  * @param comp: The comparator function used to indicate sort order
369
764
  * @param list: The list to be sorted
765
+ * @returns The sorted list
766
+ *
370
767
  * @since v0.4.5
371
768
  */
372
- let sort = (comp, list) => {
769
+ export let sort = (comp, list) => {
373
770
  let rec merge = (left, right, list) => {
374
771
  match ((left, right)) {
375
772
  (_, []) => {