@grain/stdlib 0.5.13 → 0.6.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 (155) hide show
  1. package/CHANGELOG.md +193 -0
  2. package/LICENSE +1 -1
  3. package/README.md +25 -2
  4. package/array.gr +1512 -199
  5. package/array.md +2032 -94
  6. package/bigint.gr +239 -140
  7. package/bigint.md +450 -106
  8. package/buffer.gr +595 -102
  9. package/buffer.md +903 -145
  10. package/bytes.gr +401 -110
  11. package/bytes.md +551 -63
  12. package/char.gr +228 -49
  13. package/char.md +373 -7
  14. package/exception.gr +26 -12
  15. package/exception.md +29 -5
  16. package/float32.gr +130 -109
  17. package/float32.md +185 -57
  18. package/float64.gr +112 -99
  19. package/float64.md +185 -57
  20. package/hash.gr +47 -37
  21. package/hash.md +21 -3
  22. package/int16.gr +430 -0
  23. package/int16.md +618 -0
  24. package/int32.gr +200 -269
  25. package/int32.md +254 -289
  26. package/int64.gr +142 -225
  27. package/int64.md +254 -289
  28. package/int8.gr +511 -0
  29. package/int8.md +786 -0
  30. package/json.gr +2084 -0
  31. package/json.md +608 -0
  32. package/list.gr +120 -68
  33. package/list.md +125 -80
  34. package/map.gr +560 -57
  35. package/map.md +672 -56
  36. package/marshal.gr +239 -227
  37. package/marshal.md +36 -4
  38. package/number.gr +626 -676
  39. package/number.md +738 -153
  40. package/option.gr +33 -35
  41. package/option.md +58 -42
  42. package/package.json +2 -2
  43. package/path.gr +148 -187
  44. package/path.md +47 -96
  45. package/pervasives.gr +75 -416
  46. package/pervasives.md +85 -180
  47. package/priorityqueue.gr +433 -74
  48. package/priorityqueue.md +422 -54
  49. package/queue.gr +362 -80
  50. package/queue.md +433 -38
  51. package/random.gr +67 -75
  52. package/random.md +68 -40
  53. package/range.gr +135 -63
  54. package/range.md +198 -43
  55. package/rational.gr +284 -0
  56. package/rational.md +545 -0
  57. package/regex.gr +933 -1066
  58. package/regex.md +59 -60
  59. package/result.gr +23 -25
  60. package/result.md +54 -39
  61. package/runtime/atof/common.gr +78 -82
  62. package/runtime/atof/common.md +22 -10
  63. package/runtime/atof/decimal.gr +102 -127
  64. package/runtime/atof/decimal.md +28 -7
  65. package/runtime/atof/lemire.gr +56 -71
  66. package/runtime/atof/lemire.md +9 -1
  67. package/runtime/atof/parse.gr +83 -110
  68. package/runtime/atof/parse.md +12 -2
  69. package/runtime/atof/slow.gr +28 -35
  70. package/runtime/atof/slow.md +9 -1
  71. package/runtime/atof/table.gr +19 -18
  72. package/runtime/atof/table.md +10 -2
  73. package/runtime/atoi/parse.gr +153 -136
  74. package/runtime/atoi/parse.md +50 -1
  75. package/runtime/bigint.gr +410 -517
  76. package/runtime/bigint.md +71 -57
  77. package/runtime/compare.gr +176 -85
  78. package/runtime/compare.md +31 -1
  79. package/runtime/dataStructures.gr +144 -32
  80. package/runtime/dataStructures.md +267 -31
  81. package/runtime/debugPrint.gr +34 -15
  82. package/runtime/debugPrint.md +37 -5
  83. package/runtime/equal.gr +53 -52
  84. package/runtime/equal.md +30 -1
  85. package/runtime/exception.gr +38 -47
  86. package/runtime/exception.md +10 -8
  87. package/runtime/gc.gr +23 -152
  88. package/runtime/gc.md +13 -17
  89. package/runtime/malloc.gr +31 -31
  90. package/runtime/malloc.md +11 -3
  91. package/runtime/numberUtils.gr +191 -172
  92. package/runtime/numberUtils.md +17 -9
  93. package/runtime/numbers.gr +1695 -1021
  94. package/runtime/numbers.md +1098 -134
  95. package/runtime/string.gr +540 -242
  96. package/runtime/string.md +76 -6
  97. package/runtime/unsafe/constants.gr +30 -13
  98. package/runtime/unsafe/constants.md +80 -0
  99. package/runtime/unsafe/conv.gr +55 -28
  100. package/runtime/unsafe/conv.md +41 -9
  101. package/runtime/unsafe/memory.gr +10 -30
  102. package/runtime/unsafe/memory.md +15 -19
  103. package/runtime/unsafe/tags.gr +37 -21
  104. package/runtime/unsafe/tags.md +88 -8
  105. package/runtime/unsafe/wasmf32.gr +30 -36
  106. package/runtime/unsafe/wasmf32.md +64 -56
  107. package/runtime/unsafe/wasmf64.gr +30 -36
  108. package/runtime/unsafe/wasmf64.md +64 -56
  109. package/runtime/unsafe/wasmi32.gr +49 -66
  110. package/runtime/unsafe/wasmi32.md +102 -94
  111. package/runtime/unsafe/wasmi64.gr +52 -79
  112. package/runtime/unsafe/wasmi64.md +108 -100
  113. package/runtime/utils/printing.gr +13 -15
  114. package/runtime/utils/printing.md +11 -3
  115. package/runtime/wasi.gr +294 -295
  116. package/runtime/wasi.md +62 -42
  117. package/set.gr +574 -64
  118. package/set.md +634 -54
  119. package/stack.gr +181 -64
  120. package/stack.md +271 -42
  121. package/string.gr +453 -533
  122. package/string.md +241 -151
  123. package/uint16.gr +369 -0
  124. package/uint16.md +585 -0
  125. package/uint32.gr +470 -0
  126. package/uint32.md +737 -0
  127. package/uint64.gr +471 -0
  128. package/uint64.md +737 -0
  129. package/uint8.gr +369 -0
  130. package/uint8.md +585 -0
  131. package/uri.gr +1093 -0
  132. package/uri.md +477 -0
  133. package/{sys → wasi}/file.gr +914 -500
  134. package/{sys → wasi}/file.md +454 -50
  135. package/wasi/process.gr +292 -0
  136. package/{sys → wasi}/process.md +164 -6
  137. package/wasi/random.gr +77 -0
  138. package/wasi/random.md +80 -0
  139. package/{sys → wasi}/time.gr +15 -22
  140. package/{sys → wasi}/time.md +5 -5
  141. package/immutablearray.gr +0 -929
  142. package/immutablearray.md +0 -1038
  143. package/immutablemap.gr +0 -493
  144. package/immutablemap.md +0 -479
  145. package/immutablepriorityqueue.gr +0 -360
  146. package/immutablepriorityqueue.md +0 -291
  147. package/immutableset.gr +0 -498
  148. package/immutableset.md +0 -449
  149. package/runtime/debug.gr +0 -2
  150. package/runtime/debug.md +0 -6
  151. package/runtime/unsafe/errors.gr +0 -36
  152. package/runtime/unsafe/errors.md +0 -204
  153. package/sys/process.gr +0 -254
  154. package/sys/random.gr +0 -79
  155. package/sys/random.md +0 -66
package/set.gr CHANGED
@@ -1,54 +1,53 @@
1
1
  /**
2
- * @module Set: A Set is an unordered collection of unique values. Operations on a Set mutate the internal state, so it never needs to be re-assigned.
3
- * @example import Set from "set"
4
- *
2
+ * A Set is an unordered collection of unique values. Operations on a Set mutate the internal state, so it never needs to be re-assigned.
3
+ *
4
+ * An immutable set implementation is available in the `Immutable` submodule.
5
+ * @example from "set" include Set
6
+ *
5
7
  * @since v0.3.0
6
8
  */
7
- import List from "list"
8
- import Array from "array"
9
- import { hash } from "hash"
9
+ module Set
10
+
11
+ from "list" include List
12
+ from "array" include Array
13
+ from "hash" include Hash
14
+ use Hash.{ hash }
10
15
 
11
- record Bucket<t> {
16
+ record rec Bucket<t> {
12
17
  mut key: t,
13
18
  mut next: Option<Bucket<t>>,
14
19
  }
15
20
 
16
- /**
17
- * @section Types: Type declarations included in the Set module.
18
- */
19
-
20
- record Set<k> {
21
+ abstract record Set<k> {
21
22
  mut size: Number,
22
23
  mut buckets: Array<Option<Bucket<k>>>,
23
24
  }
24
25
 
25
26
  /**
26
- * @section Values: Functions for working with Sets.
27
+ * Represents the internal state of a set.
27
28
  */
29
+ provide record InternalSetStats {
30
+ currentSize: Number,
31
+ bucketCount: Number,
32
+ }
28
33
 
29
34
  // TODO: This could take an `eq` function to custom comparisons
30
35
  /**
31
- * Creates a new empty set with an initial storage of the given size. As values are added or removed, the internal storage may grow or shrink. Generally, you won't need to care about the storage size of your set and can use `Set.make()` instead.
36
+ * Creates a new empty set with an initial storage of the given size. As
37
+ * values are added or removed, the internal storage may grow or shrink.
38
+ * Generally, you won't need to care about the storage size of your set and
39
+ * can use the default size.
32
40
  *
33
41
  * @param size: The initial storage size of the set
34
42
  * @returns An empty set with the given initial storage size
35
- *
43
+ *
36
44
  * @since v0.3.0
45
+ * @history v0.6.0: Merged with `makeSized`; modified signature to accept size
37
46
  */
38
- export let makeSized = size => {
47
+ provide let make = (size=16) => {
39
48
  let buckets = Array.make(size, None)
40
49
  { size: 0, buckets }
41
50
  }
42
- /**
43
- * Creates a new, empty set.
44
- *
45
- * @returns An empty set
46
- *
47
- * @since v0.3.0
48
- */
49
- export let make = () => {
50
- makeSized(16)
51
- }
52
51
 
53
52
  let getBucketIndex = (key, buckets) => {
54
53
  let bucketsLength = Array.length(buckets)
@@ -120,10 +119,10 @@ let rec nodeInBucket = (key, node) => {
120
119
  *
121
120
  * @param key: The value to add
122
121
  * @param set: The set to update
123
- *
122
+ *
124
123
  * @since v0.3.0
125
124
  */
126
- export let add = (key, set) => {
125
+ provide let add = (key, set) => {
127
126
  let buckets = set.buckets
128
127
  let idx = getBucketIndex(key, buckets)
129
128
  let bucket = buckets[idx]
@@ -153,10 +152,10 @@ export let add = (key, set) => {
153
152
  * @param key: The value to search for
154
153
  * @param set: The set to search
155
154
  * @returns `true` if the set contains the given value or `false` otherwise
156
- *
155
+ *
157
156
  * @since v0.3.0
158
157
  */
159
- export let contains = (key, set) => {
158
+ provide let contains = (key, set) => {
160
159
  let buckets = set.buckets
161
160
  let idx = getBucketIndex(key, buckets)
162
161
  let bucket = buckets[idx]
@@ -185,10 +184,10 @@ let rec removeInBucket = (key, node) => {
185
184
  *
186
185
  * @param key: The value to remove
187
186
  * @param set: The set to update
188
- *
187
+ *
189
188
  * @since v0.3.0
190
189
  */
191
- export let remove = (key, set) => {
190
+ provide let remove = (key, set) => {
192
191
  let buckets = set.buckets
193
192
  let idx = getBucketIndex(key, buckets)
194
193
  let bucket = buckets[idx]
@@ -213,10 +212,10 @@ export let remove = (key, set) => {
213
212
  *
214
213
  * @param set: The set to inspect
215
214
  * @returns The count of elements in the set
216
- *
215
+ *
217
216
  * @since v0.3.0
218
217
  */
219
- export let size = set => {
218
+ provide let size = set => {
220
219
  set.size
221
220
  }
222
221
 
@@ -225,10 +224,10 @@ export let size = set => {
225
224
  *
226
225
  * @param set: The set to inspect
227
226
  * @returns `true` if the given set is empty or `false` otherwise
228
- *
227
+ *
229
228
  * @since v0.3.0
230
229
  */
231
- export let isEmpty = set => {
230
+ provide let isEmpty = set => {
232
231
  size(set) == 0
233
232
  }
234
233
 
@@ -236,10 +235,10 @@ export let isEmpty = set => {
236
235
  * Resets the set by removing all values.
237
236
  *
238
237
  * @param set: The set to reset
239
- *
238
+ *
240
239
  * @since v0.3.0
241
240
  */
242
- export let clear = set => {
241
+ provide let clear = set => {
243
242
  set.size = 0
244
243
  let buckets = set.buckets
245
244
  Array.forEachi((bucket, idx) => {
@@ -262,11 +261,11 @@ let rec forEachBucket = (fn, node) => {
262
261
  *
263
262
  * @param fn: The iterator function to call with each element
264
263
  * @param set: The set to iterate
265
- *
264
+ *
266
265
  * @since v0.3.0
267
266
  * @history v0.5.0: Ensured the iterator function return type is always `Void`
268
267
  */
269
- export let forEach = (fn, set) => {
268
+ provide let forEach = (fn, set) => {
270
269
  let buckets = set.buckets
271
270
  Array.forEach(bucket => {
272
271
  forEachBucket(fn, bucket)
@@ -287,10 +286,10 @@ let rec reduceEachBucket = (fn, node, acc) => {
287
286
  * @param init: The initial value to use for the accumulator on the first iteration
288
287
  * @param set: The set to iterate
289
288
  * @returns The final accumulator returned from `fn`
290
- *
289
+ *
291
290
  * @since v0.3.0
292
291
  */
293
- export let reduce = (fn, init, set) => {
292
+ provide let reduce = (fn, init, set) => {
294
293
  let buckets = set.buckets
295
294
  let mut acc = init
296
295
  Array.forEach(bucket => {
@@ -304,10 +303,10 @@ export let reduce = (fn, init, set) => {
304
303
  *
305
304
  * @param fn: The predicate function to indicate which elements to remove from the set, where returning `false` indicates the value should be removed
306
305
  * @param set: The set to iterate
307
- *
306
+ *
308
307
  * @since v0.3.0
309
308
  */
310
- export let filter = (fn, set) => {
309
+ provide let filter = (fn, set) => {
311
310
  let keysToRemove = reduce((list, key) => if (!fn(key)) {
312
311
  [key, ...list]
313
312
  } else {
@@ -323,10 +322,10 @@ export let filter = (fn, set) => {
323
322
  *
324
323
  * @param fn: The predicate function to indicate which elements to remove from the set, where returning `true` indicates the value should be removed
325
324
  * @param set: The set to iterate
326
- *
325
+ *
327
326
  * @since v0.3.0
328
327
  */
329
- export let reject = (fn, set) => {
328
+ provide let reject = (fn, set) => {
330
329
  filter(key => !fn(key), set)
331
330
  }
332
331
 
@@ -335,10 +334,10 @@ export let reject = (fn, set) => {
335
334
  *
336
335
  * @param set: The set to convert
337
336
  * @returns A list containing all set values
338
- *
337
+ *
339
338
  * @since v0.3.0
340
339
  */
341
- export let toList = set => {
340
+ provide let toList = set => {
342
341
  reduce((list, key) => [key, ...list], [], set)
343
342
  }
344
343
 
@@ -347,10 +346,10 @@ export let toList = set => {
347
346
  *
348
347
  * @param list: The list to convert
349
348
  * @returns A set containing all list values
350
- *
349
+ *
351
350
  * @since v0.3.0
352
351
  */
353
- export let fromList = list => {
352
+ provide let fromList = list => {
354
353
  let set = make()
355
354
  List.forEach(key => {
356
355
  add(key, set)
@@ -363,10 +362,10 @@ export let fromList = list => {
363
362
  *
364
363
  * @param set: The set to convert
365
364
  * @returns An array containing all set values
366
- *
365
+ *
367
366
  * @since v0.3.0
368
367
  */
369
- export let toArray = set => {
368
+ provide let toArray = set => {
370
369
  Array.fromList(toList(set))
371
370
  }
372
371
 
@@ -375,10 +374,10 @@ export let toArray = set => {
375
374
  *
376
375
  * @param array: The array to convert
377
376
  * @returns A set containing all array values
378
- *
377
+ *
379
378
  * @since v0.3.0
380
379
  */
381
- export let fromArray = array => {
380
+ provide let fromArray = array => {
382
381
  let set = make()
383
382
  Array.forEach(key => {
384
383
  add(key, set)
@@ -392,10 +391,10 @@ export let fromArray = array => {
392
391
  * @param set1: The first set to combine
393
392
  * @param set2: The second set to combine
394
393
  * @returns A set containing all elements of both sets
395
- *
394
+ *
396
395
  * @since v0.3.0
397
396
  */
398
- export let union = (set1, set2) => {
397
+ provide let union = (set1, set2) => {
399
398
  let set = make()
400
399
  forEach(key => {
401
400
  add(key, set)
@@ -412,10 +411,10 @@ export let union = (set1, set2) => {
412
411
  * @param set1: The first set to combine
413
412
  * @param set2: The second set to combine
414
413
  * @returns A set containing only unshared elements from both sets
415
- *
414
+ *
416
415
  * @since v0.3.0
417
416
  */
418
- export let diff = (set1, set2) => {
417
+ provide let diff = (set1, set2) => {
419
418
  let set = make()
420
419
  forEach(key => {
421
420
  if (!contains(key, set2)) {
@@ -436,10 +435,10 @@ export let diff = (set1, set2) => {
436
435
  * @param set1: The first set to combine
437
436
  * @param set2: The second set to combine
438
437
  * @returns A set containing only shared elements from both sets
439
- *
438
+ *
440
439
  * @since v0.3.0
441
440
  */
442
- export let intersect = (set1, set2) => {
441
+ provide let intersect = (set1, set2) => {
443
442
  let set = make()
444
443
  forEach(key => {
445
444
  if (contains(key, set2)) {
@@ -454,15 +453,526 @@ export let intersect = (set1, set2) => {
454
453
  set
455
454
  }
456
455
 
457
- // TODO(#190): Should return a Record type instead of a Tuple
458
456
  /**
459
457
  * Provides data representing the internal state state of the set.
460
458
  *
461
459
  * @param set: The set to inspect
462
460
  * @returns The internal state of the set
463
- *
461
+ *
464
462
  * @since v0.3.0
463
+ * @history v0.6.0: Return `InternalSetStats` record instead of a tuple
464
+ */
465
+ provide let getInternalStats = set => {
466
+ { currentSize: set.size, bucketCount: Array.length(set.buckets) }
467
+ }
468
+
469
+ /**
470
+ * An immutable set implementation.
471
+ *
472
+ * @since v0.6.0
473
+ * @history v0.5.4: Originally in `"immutableset"` module
465
474
  */
466
- export let getInternalStats = set => {
467
- (set.size, Array.length(set.buckets))
475
+ provide module Immutable {
476
+ // implementation based on the paper "Implementing Sets Efficiently in a
477
+ // Functional Language" by Stephen Adams
478
+
479
+ record rec Node<a> {
480
+ key: a,
481
+ size: Number,
482
+ left: Set<a>,
483
+ right: Set<a>,
484
+ }
485
+ and abstract enum Set<a> {
486
+ Empty,
487
+ Tree(Node<a>),
488
+ }
489
+
490
+ // semi-arbitrary value chosen for algorithm for determining when to balance
491
+ // trees; no tree can have a left subtree containing this number of times
492
+ // more elements than its right subtree or vice versa
493
+ let weight = 4
494
+
495
+ /**
496
+ * An empty set
497
+ *
498
+ * @since v0.6.0
499
+ * @history v0.5.4: Originally in `"immutableset"` module
500
+ */
501
+ provide let empty = Empty
502
+
503
+ // returns the minimum value in a tree
504
+ let rec min = node => {
505
+ match (node) {
506
+ Tree({ key, left: Empty, _ }) => key,
507
+ Tree({ left, _ }) => min(left),
508
+ Empty => fail "Impossible: min of empty element in Set.Immutable",
509
+ }
510
+ }
511
+
512
+ /**
513
+ * Provides the count of values within the set.
514
+ *
515
+ * @param set: The set to inspect
516
+ * @returns The count of elements in the set
517
+ *
518
+ * @since v0.6.0
519
+ * @history v0.5.4: Originally in `"immutableset"` module
520
+ */
521
+ provide let size = set => {
522
+ match (set) {
523
+ Empty => 0,
524
+ Tree({ size, _ }) => size,
525
+ }
526
+ }
527
+
528
+ /**
529
+ * Determines if the set contains no elements.
530
+ *
531
+ * @param set: The set to inspect
532
+ * @returns `true` if the given set is empty or `false` otherwise
533
+ *
534
+ * @since v0.6.0
535
+ * @history v0.5.4: Originally in `"immutableset"` module
536
+ */
537
+ provide let isEmpty = set => {
538
+ match (set) {
539
+ Empty => true,
540
+ Tree(_) => false,
541
+ }
542
+ }
543
+
544
+ let unwrapTree = node => {
545
+ match (node) {
546
+ Empty =>
547
+ fail "Impossible: Set.Immutable unwrapTree got an empty tree node",
548
+ Tree(tree) => tree,
549
+ }
550
+ }
551
+
552
+ // helper function for creating a tree node with correct size from
553
+ // two balanced trees
554
+ let makeNode = (key, left, right) => {
555
+ Tree({ key, size: 1 + size(left) + size(right), left, right })
556
+ }
557
+
558
+ // note: see Figure 1 of paper referenced above for visual illustration of
559
+ // the rotations below
560
+
561
+ // node rotation moving the left subtree of the right node to the left side
562
+ let singleL = (key, left, right) => {
563
+ let { key: rKey, left: rl, right: rr, _ } = unwrapTree(right)
564
+ makeNode(rKey, makeNode(key, left, rl), rr)
565
+ }
566
+
567
+ // node rotation moving left child of right tree to the root
568
+ let doubleL = (key, left, right) => {
569
+ let { key: rKey, left: rl, right: rr, _ } = unwrapTree(right)
570
+ let { key: rlKey, left: rll, right: rlr, _ } = unwrapTree(rl)
571
+ makeNode(rlKey, makeNode(key, left, rll), makeNode(rKey, rlr, rr))
572
+ }
573
+
574
+ // node rotation moving the right subtree of the left node to the right side
575
+ let singleR = (key, left, right) => {
576
+ let { key: lKey, left: ll, right: lr, _ } = unwrapTree(left)
577
+ makeNode(lKey, ll, makeNode(key, lr, right))
578
+ }
579
+
580
+ // node rotation moving right child of left tree to the root
581
+ let doubleR = (key, left, right) => {
582
+ let { key: lKey, left: ll, right: lr, _ } = unwrapTree(left)
583
+ let { key: lrKey, left: lrl, right: lrr, _ } = unwrapTree(lr)
584
+ makeNode(lrKey, makeNode(lKey, ll, lrl), makeNode(key, lrr, right))
585
+ }
586
+
587
+ // creates a new node after either the left or right trees have just had an
588
+ // element inserted or removed from them, maintaining balance in the tree
589
+ let balancedNode = (key, left, right) => {
590
+ let makeNodeFn = if (size(left) + size(right) < 2) {
591
+ makeNode
592
+ } else if (size(right) > weight * size(left)) {
593
+ // if the right tree is too much larger than the left then move part of
594
+ // the right tree to the left side
595
+ let { left: rl, right: rr, _ } = unwrapTree(right)
596
+ if (size(rl) < size(rr)) singleL else doubleL
597
+ } else if (size(left) > weight * size(right)) {
598
+ // if the left tree is too much larger than the right then move part of
599
+ // the left tree to the right side
600
+ let { left: ll, right: lr, _ } = unwrapTree(left)
601
+ if (size(lr) < size(ll)) singleR else doubleR
602
+ } else {
603
+ // if neither tree is too much larger than the other then simply create
604
+ // a new node
605
+ makeNode
606
+ }
607
+
608
+ makeNodeFn(key, left, right)
609
+ }
610
+
611
+ /**
612
+ * Produces a new set by inserting the given value into the set. If the value
613
+ * already exists, the new set will have the same elements as the input set.
614
+ *
615
+ * @param key: The value to add
616
+ * @param set: The base set
617
+ * @returns A new set containing the new element
618
+ *
619
+ * @since v0.6.0
620
+ * @history v0.5.4: Originally in `"immutableset"` module
621
+ */
622
+ provide let rec add = (key, set) => {
623
+ match (set) {
624
+ Empty => Tree({ key, size: 1, left: Empty, right: Empty }),
625
+ Tree({ key: nodeKey, left, right, _ }) => {
626
+ match (compare(key, nodeKey)) {
627
+ cmp when cmp < 0 => balancedNode(nodeKey, add(key, left), right),
628
+ cmp when cmp > 0 => balancedNode(nodeKey, left, add(key, right)),
629
+ _ => makeNode(key, left, right),
630
+ }
631
+ },
632
+ }
633
+ }
634
+
635
+ /**
636
+ * Determines if the set contains the given value.
637
+ *
638
+ * @param key: The value to search for
639
+ * @param set: The set to search
640
+ * @returns `true` if the set contains the given value or `false` otherwise
641
+ *
642
+ * @since v0.6.0
643
+ * @history v0.5.4: Originally in `"immutableset"` module
644
+ */
645
+ provide let rec contains = (key, set) => {
646
+ match (set) {
647
+ Empty => false,
648
+ Tree({ key: nodeKey, left, right, _ }) => {
649
+ match (compare(key, nodeKey)) {
650
+ cmp when cmp < 0 => contains(key, left),
651
+ cmp when cmp > 0 => contains(key, right),
652
+ _ => true,
653
+ }
654
+ },
655
+ }
656
+ }
657
+
658
+ // removes the minimum element from a tree
659
+ let rec removeMin = node => {
660
+ match (node) {
661
+ Tree({ left: Empty, right, _ }) => right,
662
+ Tree({ key, left, right, _ }) =>
663
+ balancedNode(key, removeMin(left), right),
664
+ _ => fail "Impossible: Set.Immutable removeMin on empty node",
665
+ }
666
+ }
667
+
668
+ // helper function for removing a node by creating a new node containing the
669
+ // removed node's left and right subtrees
670
+ let removeInner = (left, right) => {
671
+ match ((left, right)) {
672
+ (Empty, node) | (node, Empty) => node,
673
+ (left, right) => {
674
+ balancedNode(min(right), left, removeMin(right))
675
+ },
676
+ }
677
+ }
678
+
679
+ /**
680
+ * Produces a new set without the given element. If the value doesn't exist in
681
+ * the set, the set will be returned unmodified.
682
+ *
683
+ * @param key: The value to exclude
684
+ * @param set: The set to exclude from
685
+ * @returns A new set without the excluded element
686
+ *
687
+ * @since v0.6.0
688
+ * @history v0.5.4: Originally in `"immutableset"` module
689
+ */
690
+ provide let rec remove = (key, set) => {
691
+ match (set) {
692
+ Empty => Empty,
693
+ Tree({ key: nodeKey, left, right, _ }) => {
694
+ match (compare(key, nodeKey)) {
695
+ cmp when cmp < 0 => balancedNode(nodeKey, remove(key, left), right),
696
+ cmp when cmp > 0 => balancedNode(nodeKey, left, remove(key, right)),
697
+ _ => removeInner(left, right),
698
+ }
699
+ },
700
+ }
701
+ }
702
+
703
+ /**
704
+ * Iterates the set, calling an iterator function on each element.
705
+ *
706
+ * @param fn: The iterator function to call with each element
707
+ * @param set: The set to iterate
708
+ *
709
+ * @since v0.6.0
710
+ * @history v0.5.4: Originally in `"immutableset"` module
711
+ */
712
+ provide let forEach = (fn, set) => {
713
+ let rec forEachInner = node => {
714
+ match (node) {
715
+ Empty => void,
716
+ Tree({ key, left, right, _ }) => {
717
+ forEachInner(left)
718
+ fn(key): Void
719
+ forEachInner(right)
720
+ },
721
+ }
722
+ }
723
+ forEachInner(set)
724
+ }
725
+
726
+ /**
727
+ * Combines all elements of a set using a reducer function.
728
+ *
729
+ * @param fn: The reducer function to call on each element, where the value returned will be the next accumulator value
730
+ * @param init: The initial value to use for the accumulator on the first iteration
731
+ * @param set: The set to iterate
732
+ * @returns The final accumulator returned from `fn`
733
+ *
734
+ * @since v0.6.0
735
+ * @history v0.5.4: Originally in `"immutableset"` module
736
+ */
737
+ provide let reduce = (fn, init, set) => {
738
+ let rec reduceInner = (acc, node) => {
739
+ match (node) {
740
+ Empty => acc,
741
+ Tree({ key, left, right, _ }) => {
742
+ let newAcc = fn(reduceInner(acc, left), key)
743
+ reduceInner(newAcc, right)
744
+ },
745
+ }
746
+ }
747
+ reduceInner(init, set)
748
+ }
749
+
750
+ // joins two trees with a value, preserving the BST property of left children
751
+ // being less the node and right children being greater than the node
752
+ let rec concat3 = (key, left, right) => {
753
+ match ((left, right)) {
754
+ (Empty, node) | (node, Empty) => add(key, node),
755
+ (Tree(left) as leftOpt, Tree(right) as rightOpt) => {
756
+ let { size: lSize, key: lKey, left: lLeft, right: lRight } = left
757
+ let { size: rSize, key: rKey, left: rLeft, right: rRight } = right
758
+ if (weight * lSize < rSize) {
759
+ balancedNode(rKey, concat3(key, leftOpt, rLeft), rRight)
760
+ } else if (weight * rSize < lSize) {
761
+ balancedNode(lKey, lLeft, concat3(key, lRight, rightOpt))
762
+ } else {
763
+ makeNode(key, leftOpt, rightOpt)
764
+ }
765
+ },
766
+ }
767
+ }
768
+
769
+ // returns a tree containing all of the nodes in the input tree whose values
770
+ // are less than the given value
771
+ let rec splitLt = (splitKey, node) => {
772
+ match (node) {
773
+ Empty => Empty,
774
+ Tree({ key, left, right, _ }) => {
775
+ match (compare(key, splitKey)) {
776
+ // we want this node, join it to the output
777
+ cmp when cmp < 0 => concat3(key, left, splitLt(splitKey, right)),
778
+ cmp when cmp > 0 => splitLt(splitKey, left),
779
+ _ => left,
780
+ }
781
+ },
782
+ }
783
+ }
784
+
785
+ // returns a tree containing all of the nodes in the input tree whose values
786
+ // are greater than the given value
787
+ let rec splitGt = (splitKey, node) => {
788
+ match (node) {
789
+ Empty => Empty,
790
+ Tree({ key, left, right, _ }) => {
791
+ match (compare(key, splitKey)) {
792
+ // we want this node, join it to the output
793
+ cmp when cmp > 0 => concat3(key, splitGt(splitKey, left), right),
794
+ cmp when cmp < 0 => splitGt(splitKey, right),
795
+ _ => right,
796
+ }
797
+ },
798
+ }
799
+ }
800
+
801
+ // concatenates two trees of arbitrary size
802
+ let concat = (node1, node2) => {
803
+ match (node2) {
804
+ Empty => node1,
805
+ _ => concat3(min(node2), node1, removeMin(node2)),
806
+ }
807
+ }
808
+
809
+ /**
810
+ * Produces a new set without the elements from the input set where a predicate function returns `false`.
811
+ *
812
+ * @param fn: The predicate function to indicate which elements to exclude from the set, where returning `false` indicates the value should be excluded
813
+ * @param set: The set to iterate
814
+ * @returns A new set excluding the elements not fulfilling the predicate
815
+ *
816
+ * @since v0.6.0
817
+ * @history v0.5.4: Originally in `"immutableset"` module
818
+ */
819
+ provide let filter = (fn, set) => {
820
+ let rec filterInner = node => {
821
+ match (node) {
822
+ Empty => Empty,
823
+ Tree({ key, left, right, _ }) => {
824
+ if (fn(key)) {
825
+ concat3(key, filterInner(left), filterInner(right))
826
+ } else {
827
+ concat(filterInner(left), filterInner(right))
828
+ }
829
+ },
830
+ }
831
+ }
832
+ filterInner(set)
833
+ }
834
+
835
+ /**
836
+ * Produces a new set without the elements from the input set where a predicate function returns `true`.
837
+ *
838
+ * @param fn: The predicate function to indicate which elements to exclude from the set, where returning `true` indicates the value should be excluded
839
+ * @param set: The set to iterate
840
+ * @returns A new set excluding the elements fulfilling the predicate
841
+ *
842
+ * @since v0.6.0
843
+ * @history v0.5.4: Originally in `"immutableset"` module
844
+ */
845
+ provide let reject = (fn, set) => {
846
+ filter(key => !fn(key), set)
847
+ }
848
+
849
+ /**
850
+ * Combines two sets into a single set containing all elements from both sets.
851
+ *
852
+ * @param set1: The first set to combine
853
+ * @param set2: The second set to combine
854
+ * @returns A set containing all elements of both sets
855
+ *
856
+ * @since v0.6.0
857
+ * @history v0.5.4: Originally in `"immutableset"` module
858
+ */
859
+ provide let rec union = (set1, set2) => {
860
+ match ((set1, set2)) {
861
+ (Empty, node) | (node, Empty) => node,
862
+ (node1, Tree({ key: key2, left: left2, right: right2, _ })) => {
863
+ let l = splitLt(key2, node1)
864
+ let r = splitGt(key2, node1)
865
+ concat3(key2, union(l, left2), union(r, right2))
866
+ },
867
+ }
868
+ }
869
+
870
+ /**
871
+ * Combines two sets into a single set containing only the elements not shared between both sets.
872
+ *
873
+ * @param set1: The first set to combine
874
+ * @param set2: The second set to combine
875
+ * @returns A set containing only unshared elements from both sets
876
+ *
877
+ * @since v0.6.0
878
+ * @history v0.5.4: Originally in `"immutableset"` module
879
+ */
880
+ provide let diff = (set1, set2) => {
881
+ let rec diffInner = (node1, node2) => {
882
+ match ((node1, node2)) {
883
+ (Empty, node) | (node, Empty) => node,
884
+ (node1, Tree({ key: key2, left: left2, right: right2, _ })) => {
885
+ let l = splitLt(key2, node1)
886
+ let r = splitGt(key2, node1)
887
+ concat(diffInner(l, left2), diffInner(r, right2))
888
+ },
889
+ }
890
+ }
891
+ union(diffInner(set1, set2), diffInner(set2, set1))
892
+ }
893
+
894
+ /**
895
+ * Combines two sets into a single set containing only the elements shared between both sets.
896
+ *
897
+ * @param set1: The first set to combine
898
+ * @param set2: The second set to combine
899
+ * @returns A set containing only shared elements from both sets
900
+ *
901
+ * @since v0.6.0
902
+ * @history v0.5.4: Originally in `"immutableset"` module
903
+ */
904
+ provide let rec intersect = (set1, set2) => {
905
+ match ((set1, set2)) {
906
+ (Empty, _) | (_, Empty) => Empty,
907
+ (node1, Tree({ key: key2, left: left2, right: right2, _ })) => {
908
+ let l = splitLt(key2, node1)
909
+ let r = splitGt(key2, node1)
910
+ if (contains(key2, node1)) {
911
+ concat3(key2, intersect(l, left2), intersect(r, right2))
912
+ } else {
913
+ concat(intersect(l, left2), intersect(r, right2))
914
+ }
915
+ },
916
+ }
917
+ }
918
+
919
+ /**
920
+ * Creates a set from a list.
921
+ *
922
+ * @param list: The list to convert
923
+ * @returns A set containing all list values
924
+ *
925
+ * @since v0.6.0
926
+ * @history v0.5.4: Originally in `"immutableset"` module
927
+ */
928
+ provide let fromList = list => {
929
+ List.reduce((set, key) => add(key, set), empty, list)
930
+ }
931
+
932
+ /**
933
+ * Converts a set into a list of its elements.
934
+ *
935
+ * @param set: The set to convert
936
+ * @returns A list containing all set values
937
+ *
938
+ * @since v0.6.0
939
+ * @history v0.5.4: Originally in `"immutableset"` module
940
+ */
941
+ provide let toList = set => {
942
+ let rec toListInner = (acc, node) => {
943
+ match (node) {
944
+ Empty => acc,
945
+ Tree({ key, left, right, _ }) => {
946
+ toListInner([key, ...toListInner(acc, right)], left)
947
+ },
948
+ }
949
+ }
950
+ toListInner([], set)
951
+ }
952
+
953
+ /**
954
+ * Creates a set from an array.
955
+ *
956
+ * @param array: The array to convert
957
+ * @returns A set containing all array values
958
+ *
959
+ * @since v0.6.0
960
+ * @history v0.5.4: Originally in `"immutableset"` module
961
+ */
962
+ provide let fromArray = array => {
963
+ Array.reduce((set, key) => add(key, set), empty, array)
964
+ }
965
+
966
+ /**
967
+ * Converts a set into an array of its elements.
968
+ *
969
+ * @param set: The set to convert
970
+ * @returns An array containing all set values
971
+ *
972
+ * @since v0.6.0
973
+ * @history v0.5.4: Originally in `"immutableset"` module
974
+ */
975
+ provide let toArray = set => {
976
+ Array.fromList(toList(set))
977
+ }
468
978
  }