Skip to content

Commit 218ff78

Browse files
feat(stdlib): Add Array.tryInit (#2209)
Co-authored-by: Oscar Spencer <oscar.spen@gmail.com>
1 parent 1cba005 commit 218ff78

File tree

3 files changed

+112
-2
lines changed

3 files changed

+112
-2
lines changed

compiler/test/stdlib/array.test.gr

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,20 @@ from "range" include Range
77

88
let arr = [> 1, 2, 3]
99

10+
// Array.make
11+
assert Array.make(5, 10) == [> 10, 10, 10, 10, 10]
12+
assert Array.make(0, 10) == [>]
13+
14+
// Array.init
15+
assert Array.init(5, n => n + 3) == [> 3, 4, 5, 6, 7]
16+
assert Array.init(0, n => n + 3) == [>]
17+
18+
// Array.tryInit
19+
assert Array.tryInit(5, n => Ok(n + 3)) == Ok([> 3, 4, 5, 6, 7])
20+
assert Array.tryInit(0, n => Ok(n + 3)) == Ok([>])
21+
assert Array.tryInit(3, n => if (n == 1) Err("stop") else Ok(n)) == Err("stop")
22+
assert Array.tryInit(0, n => if (n == 1) Err("stop") else Ok(n)) == Ok([>])
23+
1024
// Array.get
1125

1226
assert Array.get(1, arr) == 2

stdlib/array.gr

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,15 +120,64 @@ provide let init = (length: Number, fn: Number => a) => {
120120
let mut index = 0n
121121
for (let mut i = 0n; i < byteLength; i += 4n) {
122122
WasmI32.store(
123-
array + i,
123+
array,
124124
Memory.incRef(WasmI32.fromGrain(fn(tagSimpleNumber(index)))),
125-
_ARRAY_DATA_OFFSET
125+
_ARRAY_DATA_OFFSET + i
126126
)
127127
index += 1n
128128
}
129129
WasmI32.toGrain(array): Array<a>
130130
}
131131

132+
/**
133+
* Creates a new array where each element is initialized with the `Ok`
134+
* result value of an initializer function. The initializer is called with the
135+
* index of each element, and returns the new array if all calls to the
136+
* initializer succeed or the first error otherwise.
137+
*
138+
* @param length: The length of the new array
139+
* @param fn: The initializer function to call with each index, where the `Ok` value returned will be used to initialize each element
140+
* @returns `Ok(array)` if all elements were successfully initialized or `Err(error)` if the initializer function returned an error
141+
*
142+
* @throws InvalidArgument(String): When `length` is not an integer
143+
* @throws InvalidArgument(String): When `length` is negative
144+
*
145+
* @example Array.tryInit(5, n => Ok(n + 3)) == [> 3, 4, 5, 6, 7]
146+
* @example Array.tryInit(5, n => if (n == 1) Err("stop") else Ok(n)) == Err("stop")
147+
*
148+
* @since v0.7.0
149+
*/
150+
@unsafe
151+
provide let tryInit = (length: Number, fn: Number => Result<a, b>) => {
152+
use WasmI32.{ (+), (*), (<) }
153+
checkLength(length)
154+
let length = coerceNumberToWasmI32(length)
155+
let byteLength = length * 4n
156+
let array = allocateArray(length)
157+
let mut index = 0n
158+
for (let mut i = 0n; i < byteLength; i += 4n) {
159+
let value = fn(tagSimpleNumber(index))
160+
match (value) {
161+
Ok(value) => {
162+
WasmI32.store(
163+
array,
164+
Memory.incRef(WasmI32.fromGrain(value)),
165+
_ARRAY_DATA_OFFSET + i
166+
)
167+
index += 1n
168+
},
169+
Err(e) => {
170+
for (let mut j = 0n; j < i; j += 4n) {
171+
ignore(Memory.decRef(WasmI32.load(array, _ARRAY_DATA_OFFSET + j)))
172+
}
173+
Memory.free(array)
174+
return Err(e)
175+
},
176+
}
177+
}
178+
return Ok(WasmI32.toGrain(array): Array<a>)
179+
}
180+
132181
/**
133182
* An alias for normal syntactic array access, i.e. `array[n]`.
134183
*

stdlib/array.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,53 @@ Examples:
143143
Array.init(5, n => n + 3) == [> 3, 4, 5, 6, 7]
144144
```
145145

146+
### Array.**tryInit**
147+
148+
<details disabled>
149+
<summary tabindex="-1">Added in <code>next</code></summary>
150+
No other changes yet.
151+
</details>
152+
153+
```grain
154+
tryInit :
155+
(length: Number, fn: (Number => Result<a, b>)) => Result<Array<a>, b>
156+
```
157+
158+
Creates a new array where each element is initialized with the `Ok`
159+
result value of an initializer function. The initializer is called with the
160+
index of each element, and returns the new array if all calls to the
161+
initializer succeed or the first error otherwise.
162+
163+
Parameters:
164+
165+
|param|type|description|
166+
|-----|----|-----------|
167+
|`length`|`Number`|The length of the new array|
168+
|`fn`|`Number => Result<a, b>`|The initializer function to call with each index, where the `Ok` value returned will be used to initialize each element|
169+
170+
Returns:
171+
172+
|type|description|
173+
|----|-----------|
174+
|`Result<Array<a>, b>`|`Ok(array)` if all elements were successfully initialized or `Err(error)` if the initializer function returned an error|
175+
176+
Throws:
177+
178+
`InvalidArgument(String)`
179+
180+
* When `length` is not an integer
181+
* When `length` is negative
182+
183+
Examples:
184+
185+
```grain
186+
Array.tryInit(5, n => Ok(n + 3)) == [> 3, 4, 5, 6, 7]
187+
```
188+
189+
```grain
190+
Array.tryInit(5, n => if (n == 1) Err("stop") else Ok(n)) == Err("stop")
191+
```
192+
146193
### Array.**get**
147194

148195
<details>

0 commit comments

Comments
 (0)