Language reference
Arrays and mapping
Create arrays, transform their elements and control local write access.
An array type is written [T]. Array lengths may vary at runtime; its elements keep one type. Use arrays for indexed collections and callbacks; use tensors for shaped numerical operations. Conversions between them are explicit.
Create and read an array
fn main() -> int:
values: [int] = [10, 20, 30]
values[1] + length(values) This returns 23: the element at index 1 is 20, and the array has length 3. Indexes are zero-based and bounds-checked. Empty arrays need an element type from an annotation or surrounding context. Arrays can contain the supported numeric, Bool, String, record, enum and function-value types; this does not imply arbitrary nested arrays or arrays of tensors.
Map sequentially
fn double(value: int) -> int:
value * 2
fn main() -> [int]:
[1, 2, 3] -> map(double) This returns [2, 4, 6]. map evaluates its callback sequentially and preserves input order. A supported callback can capture immutable values or return another supported element type. For CPU task scheduling, choose parallelMap explicitly.
Construct by index
generate constructs a fresh array by calling a function with each index. It does not require an intermediate array of indices.
fn main() -> [int]:
offset = 10
generate(4, fn(index): index * 2 + offset) The result is [10, 12, 14, 16]. Indices run from zero up to count, excluding count, in ascending order. A zero count calls no callbacks. The count is a nonnegative int; invalid runtime counts and excessive allocation sizes produce checked faults. As with map, a callback must return an ordinary supported array element rather than a task or expected-error result.
Copy array storage
copy(values) returns an independent array of numeric, Bool or String elements. It preserves length, order and exact numeric bits. String text remains immutable and may share its own backing. copy does not accept records, function values or nested arrays.
Replace an array binding
A numeric or Bool array name can be reassigned. CPU functions support this across task suspension and expected-error handling; GPU helpers support reassignment of arrays constructed inside the helper. The new array may have a different length, but its element type stays fixed. Reassignment updates that local name; previous aliases still refer to their original storage. This differs from sort(values), which writes the existing array.
fn main() -> [int]:
values = [3, 1, 2]
original = values
values = sorted(values)
values = concat(values, [4])
[original[0], length(values), values[0]] fn main() -> [Bool]:
flags = [true, false]
original = flags
flags = [false]
[original[0], flags[0]] String, record and function-array bindings cannot be reassigned. Element assignment and sort on existing numeric or Bool storage follow the separate rules below.
A task continues reading the storage it received even when the parent replaces its local name. Fresh replacement storage can be modified independently. An alias of the old array remains protected until its task readers settle.
fn readFirst(values: [int]) -> int:
values[0]
fn main() -> int ! TaskError:
values = [3, 1, 2]
oldFirst = try parallel:
reader = spawn readFirst(values)
values = sorted(values)
values[0] = 9
try await reader
oldFirst * 100 + values[0] Slice and concatenate
fn main() -> [int]:
values: [int] = [1, 2, 3, 4]
concat(slice(values, 0, 2), [9]) This returns [1, 2, 9]. slice includes the start index and excludes the end index; both are checked. concat requires matching element types. Both allocate fresh outer array storage and preserve element order.
Sorting
sort(values) orders a numeric or Bool array in place and returns that same array. sorted(values) returns an independent sorted array. Use sort when aliases should observe the changes; use sorted to preserve the input. Both use the same deterministic ordering, including NaNs and signed zeros.
Enter a numerical pipeline
Use tensor(array) for a rank-one numeric tensor, or tensor(array, dimensions) for a checked row-major shape. array(tensor) converts supported numeric tensor contents back into a flat array. Neither conversion changes the numeric element type.
Both conversions copy numerical storage. Editing a source array cannot change an immutable tensor snapshot, and editing an array produced from a tensor cannot change that tensor.
Edit local elements
A fresh numeric or Bool array supports checked element assignment. A plain binding aliases the same backing; copy creates independent outer storage. A helper that writes caller-owned storage declares a mut parameter.
fn update(mut values: [int]) -> int:
values[1] = 9
values[1]
fn main() -> int:
values = [1, 2, 3]
alias = values
snapshot = copy(values)
updated = update(values)
updated * 100 + alias[1] * 10 + snapshot[1] This returns 992: values and alias both see the edit, while snapshot still contains 2 at index 1. Element assignment is a statement. Supply a separate result expression at the end of a function, and preserve the array's element type.
The destination and index are evaluated and bounds-checked before the right-hand side. A failing right-hand side does not perform that assignment. Writes completed before a later fault or cancellation remain visible; in-place updates do not roll back.
Wait for shared readers
Ordinary parameters and captured arrays are read-only. Parent-provided task inputs stay read-only until their readers settle, including reads through aliases and aggregate fields. Await every relevant reader before writing, or edit an independent copy instead.
fn read(values: [int]) -> int:
values[0] + values[1]
fn main() -> int ! TaskError:
values = [1, 2]
try parallel:
pending = spawn read(values)
observed = try await pending
values[0] = observed + 1
values[0] This returns 4. The write is allowed because pending has been awaited. If the write appeared before await, the checker would reject it even through an alias.
Replacing String, record or function-array elements is not supported. Tensors retain value semantics. Numeric and Bool array operations also run inside GPU helpers on arrays constructed there, including element writes and sorting under the same access rules. A GPU task can capture existing numeric and Bool arrays as read-only parameters. The task packs and uploads those arrays before executing its device helper. Await its readers before modifying the originals or their aliases. GPU helpers can return numeric/Bool scalars or flat numeric/Bool arrays. Awaiting an array result produces a native CPU array and includes transfer/decoding costs. Returning a captured array preserves its original backing; copy and sorted create independent backing. Use explicit tensor transfers for resident tensor calculations.