@grain/stdlib 0.5.2 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/marshal.md ADDED
@@ -0,0 +1,76 @@
1
+ ---
2
+ title: Marshal
3
+ ---
4
+
5
+ Utilities for serializing and deserializing Grain data.
6
+
7
+ <details disabled>
8
+ <summary tabindex="-1">Added in <code>0.5.3</code></summary>
9
+ No other changes yet.
10
+ </details>
11
+
12
+ ```grain
13
+ import Marshal from "marshal"
14
+ ```
15
+
16
+ ## Values
17
+
18
+ Functions for marshaling and unmarshaling data.
19
+
20
+ ### Marshal.**marshal**
21
+
22
+ <details disabled>
23
+ <summary tabindex="-1">Added in <code>0.5.3</code></summary>
24
+ No other changes yet.
25
+ </details>
26
+
27
+ ```grain
28
+ marshal : a -> Bytes
29
+ ```
30
+
31
+ Serialize a value into a byte-based representation suitable for transmission
32
+ across a network or disk storage. The byte-based representation can be
33
+ deserialized at a later time to restore the value.
34
+
35
+ Parameters:
36
+
37
+ |param|type|description|
38
+ |-----|----|-----------|
39
+ |`value`|`a`|The value to serialize|
40
+
41
+ Returns:
42
+
43
+ |type|description|
44
+ |----|-----------|
45
+ |`Bytes`|A byte-based representation of the value|
46
+
47
+ ### Marshal.**unmarshal**
48
+
49
+ <details disabled>
50
+ <summary tabindex="-1">Added in <code>0.5.3</code></summary>
51
+ No other changes yet.
52
+ </details>
53
+
54
+ ```grain
55
+ unmarshal : Bytes -> Result<a, String>
56
+ ```
57
+
58
+ Deserialize the byte-based representation of a value back into an in-memory
59
+ value. This operation is not type-safe, and it is recommended that a type
60
+ annotation is used to declare the type of the unmarshaled value. While
61
+ attempts to unmarshal bad data will fail, this operation is still generally
62
+ unsafe and great care should be taken to ensure that the data being
63
+ unmarshaled corresponds to the expected type.
64
+
65
+ Parameters:
66
+
67
+ |param|type|description|
68
+ |-----|----|-----------|
69
+ |`bytes`|`Bytes`|The data to deserialize|
70
+
71
+ Returns:
72
+
73
+ |type|description|
74
+ |----|-----------|
75
+ |`Result<a, String>`|An in-memory value|
76
+
package/number.gr CHANGED
@@ -13,6 +13,8 @@ import {
13
13
  coerceNumberToWasmF64,
14
14
  reducedInteger,
15
15
  isFloat,
16
+ isInteger,
17
+ isRational,
16
18
  isBoxedNumber,
17
19
  } from "runtime/numbers"
18
20
  import { parseInt } from "runtime/stringUtils"
@@ -232,6 +234,45 @@ export let abs = (x: Number) => if (0 > x) x * -1 else x
232
234
  */
233
235
  export let neg = (x: Number) => x * -1
234
236
 
237
+ /**
238
+ * Checks if a number is a floating point value.
239
+ *
240
+ * @param x: The number to check
241
+ * @returns `true` if the value is a floating point number or `false` otherwise
242
+ *
243
+ * @since v0.5.3
244
+ */
245
+ @unsafe
246
+ export let isFloat = (x: Number) => {
247
+ isFloat(WasmI32.fromGrain(x))
248
+ }
249
+
250
+ /**
251
+ * Checks if a number is an integer.
252
+ *
253
+ * @param x: The number to check
254
+ * @returns `true` if the value is an integer or `false` otherwise
255
+ *
256
+ * @since v0.5.3
257
+ */
258
+ @unsafe
259
+ export let isInteger = (x: Number) => {
260
+ isInteger(WasmI32.fromGrain(x))
261
+ }
262
+
263
+ /**
264
+ * Checks if a number is a non-integer rational value.
265
+ *
266
+ * @param x: The number to check
267
+ * @returns `true` if the value is a non-integer rational number or `false` otherwise
268
+ *
269
+ * @since v0.5.3
270
+ */
271
+ @unsafe
272
+ export let isRational = (x: Number) => {
273
+ isRational(WasmI32.fromGrain(x))
274
+ }
275
+
235
276
  /**
236
277
  * Checks if a number is finite.
237
278
  * All values are finite exept for floating point NaN, infinity or negative infinity.
package/number.md CHANGED
@@ -20,7 +20,7 @@ Number constant values.
20
20
  ### Number.**pi**
21
21
 
22
22
  <details disabled>
23
- <summary tabindex="-1">Added in <code>next</code></summary>
23
+ <summary tabindex="-1">Added in <code>0.5.2</code></summary>
24
24
  No other changes yet.
25
25
  </details>
26
26
 
@@ -33,7 +33,7 @@ Pi represented as a Number value.
33
33
  ### Number.**tau**
34
34
 
35
35
  <details disabled>
36
- <summary tabindex="-1">Added in <code>next</code></summary>
36
+ <summary tabindex="-1">Added in <code>0.5.2</code></summary>
37
37
  No other changes yet.
38
38
  </details>
39
39
 
@@ -46,7 +46,7 @@ Tau represented as a Number value.
46
46
  ### Number.**e**
47
47
 
48
48
  <details disabled>
49
- <summary tabindex="-1">Added in <code>next</code></summary>
49
+ <summary tabindex="-1">Added in <code>0.5.2</code></summary>
50
50
  No other changes yet.
51
51
  </details>
52
52
 
@@ -425,6 +425,81 @@ Returns:
425
425
  |----|-----------|
426
426
  |`Number`|The negated operand|
427
427
 
428
+ ### Number.**isFloat**
429
+
430
+ <details disabled>
431
+ <summary tabindex="-1">Added in <code>0.5.3</code></summary>
432
+ No other changes yet.
433
+ </details>
434
+
435
+ ```grain
436
+ isFloat : Number -> Bool
437
+ ```
438
+
439
+ Checks if a number is a floating point value.
440
+
441
+ Parameters:
442
+
443
+ |param|type|description|
444
+ |-----|----|-----------|
445
+ |`x`|`Number`|The number to check|
446
+
447
+ Returns:
448
+
449
+ |type|description|
450
+ |----|-----------|
451
+ |`Bool`|`true` if the value is a floating point number or `false` otherwise|
452
+
453
+ ### Number.**isInteger**
454
+
455
+ <details disabled>
456
+ <summary tabindex="-1">Added in <code>0.5.3</code></summary>
457
+ No other changes yet.
458
+ </details>
459
+
460
+ ```grain
461
+ isInteger : Number -> Bool
462
+ ```
463
+
464
+ Checks if a number is an integer.
465
+
466
+ Parameters:
467
+
468
+ |param|type|description|
469
+ |-----|----|-----------|
470
+ |`x`|`Number`|The number to check|
471
+
472
+ Returns:
473
+
474
+ |type|description|
475
+ |----|-----------|
476
+ |`Bool`|`true` if the value is an integer or `false` otherwise|
477
+
478
+ ### Number.**isRational**
479
+
480
+ <details disabled>
481
+ <summary tabindex="-1">Added in <code>0.5.3</code></summary>
482
+ No other changes yet.
483
+ </details>
484
+
485
+ ```grain
486
+ isRational : Number -> Bool
487
+ ```
488
+
489
+ Checks if a number is a non-integer rational value.
490
+
491
+ Parameters:
492
+
493
+ |param|type|description|
494
+ |-----|----|-----------|
495
+ |`x`|`Number`|The number to check|
496
+
497
+ Returns:
498
+
499
+ |type|description|
500
+ |----|-----------|
501
+ |`Bool`|`true` if the value is a non-integer rational number or `false` otherwise|
502
+
428
503
  ### Number.**isFinite**
429
504
 
430
505
  <details disabled>
@@ -538,7 +613,7 @@ Returns:
538
613
  ### Number.**sin**
539
614
 
540
615
  <details disabled>
541
- <summary tabindex="-1">Added in <code>next</code></summary>
616
+ <summary tabindex="-1">Added in <code>0.5.2</code></summary>
542
617
  No other changes yet.
543
618
  </details>
544
619
 
@@ -563,7 +638,7 @@ Returns:
563
638
  ### Number.**cos**
564
639
 
565
640
  <details disabled>
566
- <summary tabindex="-1">Added in <code>next</code></summary>
641
+ <summary tabindex="-1">Added in <code>0.5.2</code></summary>
567
642
  No other changes yet.
568
643
  </details>
569
644
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grain/stdlib",
3
- "version": "0.5.2",
3
+ "version": "0.5.3",
4
4
  "description": "The standard library for the Grain language.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://grain-lang.org",
package/pervasives.gr CHANGED
@@ -11,6 +11,7 @@ import Memory from "runtime/unsafe/memory"
11
11
  import WasmI32 from "runtime/unsafe/wasmi32"
12
12
 
13
13
  import { equal as (==) } from "runtime/equal"
14
+ import { compare } from "runtime/compare"
14
15
 
15
16
  import {
16
17
  incr,
@@ -218,6 +219,19 @@ export (<=)
218
219
  */
219
220
  export (>=)
220
221
 
222
+ /**
223
+ * Compares the first argument to the second argument and produces an integer result.
224
+ * Provides a consistent ordering over all types and is suitable for sorting and other kinds of ordering.
225
+ * `compare` treats `NaN` differently than the other comparison operators in that it considers `NaN` equal to itself and smaller than any other number.
226
+ *
227
+ * @param num1: The first operand
228
+ * @param num2: The second operand
229
+ * @returns A negative integer if the first operand is less than the second operand, `0` if they are equal, or a positive integer otherwise
230
+ *
231
+ * @since v0.5.3
232
+ */
233
+ export compare
234
+
221
235
  /**
222
236
  * @section Math operations: Infix functions for working with Number values.
223
237
  */
@@ -410,7 +424,7 @@ export (>>)
410
424
 
411
425
  // Number coercions & conversions
412
426
 
413
- // [TODO] (#311) Commented until we nail down semantics
427
+ // TODO(#311): Commented until we nail down semantics
414
428
  // import foreign wasm convertExactToInexact : Number -> Number as inexact from "stdlib-external/runtime"
415
429
  // import foreign wasm convertInexactToExact : Number -> Number as exact from "stdlib-external/runtime"
416
430
 
@@ -553,10 +567,7 @@ export primitive unbox: Box<a> -> a = "@unbox"
553
567
  * @since v0.4.0
554
568
  */
555
569
  export let cons = (a, b) =>
556
- [
557
- a,
558
- ...b
559
- ] // <- workaround for (grain-lang/grain#802) [TODO] fix #802 and delete
570
+ [a, ...b] // TODO(#802): Remove workaround after 802 is completed
560
571
  /**
561
572
  * The empty list syntax (`[]`) provided as a value.
562
573
  *
package/pervasives.md CHANGED
@@ -369,6 +369,34 @@ Returns:
369
369
  |----|-----------|
370
370
  |`Bool`|`true` if the first operand is greater than or equal to the second operand or `false` otherwise|
371
371
 
372
+ ### Pervasives.**compare**
373
+
374
+ <details disabled>
375
+ <summary tabindex="-1">Added in <code>0.5.3</code></summary>
376
+ No other changes yet.
377
+ </details>
378
+
379
+ ```grain
380
+ compare : (a, a) -> Number
381
+ ```
382
+
383
+ Compares the first argument to the second argument and produces an integer result.
384
+ Provides a consistent ordering over all types and is suitable for sorting and other kinds of ordering.
385
+ `compare` treats `NaN` differently than the other comparison operators in that it considers `NaN` equal to itself and smaller than any other number.
386
+
387
+ Parameters:
388
+
389
+ |param|type|description|
390
+ |-----|----|-----------|
391
+ |`num1`|`a`|The first operand|
392
+ |`num2`|`a`|The second operand|
393
+
394
+ Returns:
395
+
396
+ |type|description|
397
+ |----|-----------|
398
+ |`Number`|A negative integer if the first operand is less than the second operand, `0` if they are equal, or a positive integer otherwise|
399
+
372
400
  ## Math operations
373
401
 
374
402
  Infix functions for working with Number values.
@@ -0,0 +1,241 @@
1
+ /**
2
+ * @module PriorityQueue: A mutable priority queue implementation. A priority queue is a data structure that maintains elements in a priority order. Elements with higher priority are served before elements with lower priority when extracting from the priority queue.
3
+ *
4
+ * @example import PriorityQueue from "priorityqueue"
5
+ *
6
+ * @since v0.5.3
7
+ */
8
+
9
+ import Array from "array"
10
+ import List from "list"
11
+ import Number from "number"
12
+ import Option from "option"
13
+
14
+ /**
15
+ * @section Types: Type declarations included in the PriorityQueue module.
16
+ */
17
+
18
+ /**
19
+ * Mutable data structure which maintains a priority order for its elements.
20
+ */
21
+ record PriorityQueue<a> {
22
+ mut size: Number,
23
+ mut array: Array<Option<a>>,
24
+ comp: (a, a) -> Number,
25
+ }
26
+
27
+ /**
28
+ * @section Values: Functions for working with PriorityQueues.
29
+ */
30
+
31
+ let swap = (i1, i2, array) => {
32
+ let t = array[i2]
33
+ array[i2] = array[i1]
34
+ array[i1] = t
35
+ }
36
+
37
+ let get = (array, i) =>
38
+ Option.expect(
39
+ "Impossible: " ++
40
+ toString(i) ++
41
+ " in PriorityQueue's inner storage array is None",
42
+ array[i]
43
+ )
44
+
45
+ let rec siftDown = (i, pq) => {
46
+ let leftI = 2 * i + 1
47
+ let rightI = 2 * i + 2
48
+
49
+ // we want to find the smaller child from the current tree node to sift down to
50
+ let mut swapWithI = i
51
+ if (leftI < pq.size && pq.comp(get(pq.array, leftI), get(pq.array, i)) < 0) {
52
+ swapWithI = leftI
53
+ }
54
+ if (
55
+ rightI < pq.size &&
56
+ pq.comp(get(pq.array, rightI), get(pq.array, swapWithI)) < 0
57
+ ) {
58
+ swapWithI = rightI
59
+ }
60
+ if (swapWithI != i) {
61
+ swap(i, swapWithI, pq.array)
62
+ siftDown(swapWithI, pq)
63
+ }
64
+ }
65
+
66
+ let rec siftUp = (i, pq) => {
67
+ let parentI = Number.trunc((i - 1) / 2)
68
+ // we should only sift up if the element is smaller than its parent
69
+ if (i > 0 && pq.comp(get(pq.array, i), get(pq.array, parentI)) < 0) {
70
+ swap(i, parentI, pq.array)
71
+ siftUp(parentI, pq)
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Creates a new priority queue with a given internal storage size and a
77
+ * comparator function, which is used to determine priority of elements. The
78
+ * comparator function takes two elements and must return 0 if both share
79
+ * priority, a positive number if the first has greater priority, and a
80
+ * negative number if the first has less priority.
81
+ *
82
+ * Generally, you won't need to care about the storage size of your priority
83
+ * queue and can use `PriorityQueue.make()` instead.
84
+ *
85
+ * @param size: The initial storage size of the priority queue
86
+ * @param comp: The comparator function used to indicate priority order
87
+ * @returns An empty priority queue
88
+ *
89
+ * @since v0.5.3
90
+ */
91
+ export let makeSized = (size, comp) => {
92
+ { size: 0, array: Array.make(size, None), comp }
93
+ }
94
+
95
+ /**
96
+ * Creates a new priority queue with a comparator function, which is used to
97
+ * determine priority of elements. The comparator function takes two elements
98
+ * and must return 0 if both share priority, a positive number if the first
99
+ * has greater priority, and a negative number if the first has less priority.
100
+ *
101
+ * @param comp: The comparator function used to indicate priority order
102
+ * @returns An empty priority queue
103
+ *
104
+ * @example PriorityQueue.make(compare) // creates a min priority queue of numbers using the compare pervasive
105
+ * @example PriorityQueue.make((a, b) => String.length(b) - String.length(a)) // creates a priority queue by string length (longest to shortest)
106
+ *
107
+ * @since v0.5.3
108
+ */
109
+ export let make = comp => {
110
+ makeSized(16, comp)
111
+ }
112
+
113
+ /**
114
+ * Gets the number of elements in a priority queue.
115
+ *
116
+ * @param pq: The priority queue to inspect
117
+ * @returns The number of elements in the priority queue
118
+ *
119
+ * @since v0.5.3
120
+ */
121
+ export let size = pq => {
122
+ pq.size
123
+ }
124
+
125
+ /**
126
+ * Determines if the priority queue contains no elements.
127
+ *
128
+ * @param pq: The priority queue to check
129
+ * @returns `true` if the priority queue is empty and `false` otherwise
130
+ *
131
+ * @since v0.5.3
132
+ */
133
+ export let isEmpty = pq => {
134
+ pq.size == 0
135
+ }
136
+
137
+ /**
138
+ * Adds a new element to the priority queue.
139
+ *
140
+ * @param val: The value to add into the priority queue
141
+ * @param pq: The priority queue to update
142
+ *
143
+ * @since v0.5.3
144
+ */
145
+ export let push = (val, pq) => {
146
+ let arrLen = Array.length(pq.array)
147
+ // double size of internal array if out of space
148
+ if (pq.size == arrLen) {
149
+ let oldArr = pq.array
150
+ pq.array = Array.make(arrLen * 2, None)
151
+ Array.forEachi((val, i) => {
152
+ pq.array[i] = val
153
+ }, oldArr)
154
+ }
155
+ pq.array[pq.size] = Some(val)
156
+ pq.size += 1
157
+ // reorder heap to ensure that binary heap property of parent nodes having
158
+ // larger values than their children is upheld
159
+ siftUp(pq.size - 1, pq)
160
+ }
161
+
162
+ /**
163
+ * Retrieves the highest priority element in the priority queue. It is not
164
+ * removed from the queue.
165
+ *
166
+ * @param pq: The priority queue to inspect
167
+ * @returns `Some(value)` containing the highest priority element or `None` if the priority queue is empty
168
+ *
169
+ * @since v0.5.3
170
+ */
171
+ export let peek = pq => {
172
+ if (pq.size == 0) {
173
+ None
174
+ } else {
175
+ pq.array[0]
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Removes and retrieves the highest priority element in the priority queue.
181
+ *
182
+ * @param pq: The priority queue to inspect
183
+ * @returns `Some(value)` containing the highest priority element or `None` if the priority queue is empty
184
+ *
185
+ * @since v0.5.3
186
+ */
187
+ export let pop = pq => {
188
+ if (pq.size == 0) {
189
+ None
190
+ } else {
191
+ let root = pq.array[0]
192
+
193
+ pq.array[0] = pq.array[pq.size - 1]
194
+ pq.array[pq.size - 1] = None
195
+ pq.size -= 1
196
+ // reorder heap to ensure that binary heap property of parent nodes having
197
+ // larger values than their children is upheld
198
+ siftDown(0, pq)
199
+ root
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Clears the priority queue and produces a list of all of the elements in the priority
205
+ * queue in priority order.
206
+ *
207
+ * @param pq: The priority queue to drain
208
+ * @returns A list of all elements in the priority in priority order
209
+ *
210
+ * @since v0.5.3
211
+ */
212
+ export let drain = pq => {
213
+ let rec drainRec = acc => {
214
+ match (pop(pq)) {
215
+ Some(val) => drainRec([val, ...acc]),
216
+ None => acc,
217
+ }
218
+ }
219
+ List.reverse(drainRec([]))
220
+ }
221
+
222
+ /**
223
+ * Constructs a new priority queue initialized with the elements in the list
224
+ * using a custom comparator function, which is used to determine priority of
225
+ * elements. The comparator function takes two elements and must return 0 if
226
+ * both share priority, a positive number if the first has greater priority,
227
+ * and a negative number if the first has less priority.
228
+ *
229
+ * @param list: A list of values used to initialize the priority queue
230
+ * @param comp: A comparator function used to assign priority to elements
231
+ * @returns A priority queue containing the elements from the list
232
+ *
233
+ * @since v0.5.3
234
+ */
235
+ export let fromList = (list, comp) => {
236
+ let heap = makeSized(List.length(list), comp)
237
+ List.forEach(val => {
238
+ push(val, heap)
239
+ }, list)
240
+ heap
241
+ }