Skip to content
Noodle
InstallLearnPlayground
GitHub

Arrays

Array[T] is the standard immutable ordered collection. MutArray[T] is its mutable counterpart. The representations are language-defined, while the source names, methods, iteration provider, safe indexing protocol, equality, and debugging support come from the standard library.

func main() -> Unit do
values = [1, 2, 3];
empty : Array[Int] = [];
Debug.trace(values);
Debug.trace(empty);
end

Every element of a nonempty literal must have one common type. An empty literal does not determine its element type, so it needs an annotation or another nearby expected Array[T] type.

Array values are immutable: reading or passing an Array[T] never grants the ability to replace an element.

length() returns an Int. Use foreach when the element index has no meaning:

func print_all(values : Array[String]) -> Unit do
foreach value in values do
Console.println(value);
end;
end

The standard array iterable visits elements in increasing index order.

map returns a new array, applying its function from left to right. reversed returns a new array and leaves its receiver unchanged:

func main() -> Unit do
values = [1, 2, 3];
doubled = values.map(|value| value * 2);
reversed = values.reversed();
Debug.trace(doubled);
Debug.trace(reversed);
Debug.trace(values);
end

map is generic in its result element type, so a transformation may change Array[T] into Array[U].

Checked access uses values[index]!. It returns the element when the index is valid and propagates IndexError::OutOfBounds otherwise:

func item_at(values : Array[Int], index : Int) throws IndexError -> Int do
values[index]!
end
test "checked indexing" do
Testing.assert_equal(item_at([10, 20], 1)!, 20)!;
end

The receiver and index are evaluated once. The shared IndexError carries the requested index and current length.

unsafe_get(index) skips the check. Use it only when a nearby invariant proves the index is valid and the function intentionally remains infallible. “The caller should pass a valid index” is not a sufficient local invariant.

Convert an immutable array with to_mut() when elements must be appended or replaced:

func update_first(values : MutArray[Int]) throws IndexError -> Unit do
values[0]! := 40;
end
test "mutable array snapshots" do
original = [1, 2];
mutable = original.to_mut();
mutable.push(3);
update_first(mutable)!;
snapshot = mutable.to_immut();
Testing.assert_equal(original, [1, 2])!;
Testing.assert_equal(snapshot, [40, 2, 3])!;
end

to_mut() creates a distinct mutable copy; changing it cannot affect the original. Two bindings of the same MutArray alias the same mutable storage. to_immut() creates a snapshot, and later mutations cannot change that snapshot.

push appends one element. pop removes and returns the final element as Option[T]. Checked assignment uses mutable[index]! := value; and propagates IndexError on failure. unsafe_set has the same strict in-bounds precondition as unsafe_get.

MutArray is not directly iterable. If a traversal should see the elements present at one moment, iterate over mutable.to_immut() and accept the snapshot cost. Keep indexed mutation when mutable identity is part of the algorithm.

Next: Option and Result.