Skip to content

Language Methods

Language methods are called with dot syntax on the core data types — arrays (#[...]), strings ("..."), dicts (#{...}), and the iterators they produce via .iter().

MethodReturnsMeaning
length()NumberElement count
push(value)ArrayAppend element (mutates)
pop()ValueRemove and return last element (mutates)
get(index)ValueElement at zero-based index
slice(start, end)ArraySub-array [start, end)
concat(other)ArrayNew array with both arrays’ elements
reverse()ArrayReversed copy
contains(value)BooleanWhether the value is present
map(fn)ArrayTransform each element
filter(fn)ArrayKeep elements passing a predicate
reduce(fn, init)ValueFold to a single value
iter()IteratorIterator over elements

push and pop mutate the array in-place; all other methods leave the receiver untouched and return a new value.

Return the number of elements.

let n = #[1, 2, 3].length(); // 3

Append an element to the end. Mutates the array and returns it for chaining.

let a = #[1, 2];
a.push(3); // a is now #[1, 2, 3]

Remove and return the last element. Mutates the array; errors if the array is empty.

let a = #[1, 2, 3];
let last = a.pop(); // 3; a is now #[1, 2]

Return the element at a zero-based index. Errors if the index is out of bounds — same as a[index].

let x = #[10, 20, 30].get(1); // 20

Extract a sub-array from start (inclusive) to end (exclusive). Indices are clamped to the array bounds.

let mid = #[10, 20, 30, 40].slice(1, 3); // #[20, 30]

Return a new array with the elements of both arrays.

let all = #[1, 2].concat(#[3, 4]); // #[1, 2, 3, 4]

Return a reversed copy.

let back = #[1, 2, 3].reverse(); // #[3, 2, 1]

Check whether a value is present (numbers, strings, booleans, notes).

let yes = #[C4, E4, G4].contains(E4); // true

Apply fn(element) to each element and collect the results into a new array.

let doubled = #[1, 2, 3].map(fn(x) { return x * 2; }); // #[2, 4, 6]

Keep only elements for which fn(element) returns a truthy value.

let evens = #[1, 2, 3, 4].filter(fn(x) { return x % 2 == 0; }); // #[2, 4]

Fold the array to a single value with fn(accumulator, element), starting from init. Note the function comes first.

let sum = #[1, 2, 3].reduce(fn(acc, x) { return acc + x; }, 0); // 6

Create an iterator over the elements.

let firsts = #[1, 2, 3, 4].iter().take(2).collect(); // #[1, 2]
MethodReturnsMeaning
length()NumberCharacter count (Unicode-aware)
uppercase()StringUppercased copy
lowercase()StringLowercased copy
substring(start, end)StringCharacters [start, end)
split(delimiter)ArraySubstrings between delimiters
contains(needle)BooleanWhether the substring occurs
trim()StringLeading/trailing whitespace removed
replace(old, new)StringAll occurrences replaced
starts_with(prefix)BooleanWhether the string begins with prefix
ends_with(suffix)BooleanWhether the string ends with suffix
repeat(count)StringString repeated count times
iter()IteratorIterator over characters

Strings are immutable — every method returns a new value. Indices count Unicode characters, not bytes.

Return the character count.

let n = "héllo".length(); // 5

Return a case-converted copy.

let up = "kick".uppercase(); // "KICK"
let down = "SNARE".lowercase();// "snare"

Extract characters from start (inclusive) to end (exclusive). Indices are clamped to the string length.

let head = "hello world".substring(0, 5); // "hello"

Split into an array of substrings on a delimiter.

let parts = "kick,snare,hat".split(","); // #["kick", "snare", "hat"]

Test whether a substring occurs.

let yes = "hello world".contains("world"); // true

Remove leading and trailing whitespace.

let clean = " text ".trim(); // "text"

Replace every occurrence of old with new.

let s = "a-b-c".replace("-", "_"); // "a_b_c"

Test the beginning or end of the string.

let a = "drum_kick.wav".starts_with("drum"); // true
let b = "drum_kick.wav".ends_with(".wav"); // true

Repeat the string count times (negative counts give an empty string).

let beat = "x.".repeat(4); // "x.x.x.x."

Create an iterator over the characters, each yielded as a one-character string.

let chars = "abc".iter().collect(); // #["a", "b", "c"]
MethodReturnsMeaning
get(key)ValueValue for key, or NUL if missing
set(key, value)DictSet entry (mutates)
remove(key)ValueRemove key, return its value or NUL
keys()ArrayAll keys
values()ArrayAll values
length()NumberEntry count
contains_key(key)BooleanWhether the key exists
entries()Array[key, value] pairs
merge(other)DictNew dict, other’s entries win
map(fn)DictTransform values, keep keys
filter(fn)DictKeep entries passing a predicate
reduce(fn, init)ValueFold to a single value
iter()IteratorIterator over keys

Keys are strings or numbers. set and remove mutate the dict in-place; merge, map, and filter return new dicts. Unlike array.get, dict.get returns NUL for a missing key instead of erroring.

Return the value for a key, or NUL if it is missing.

let d = #{"name": "kick"};
let name = d.get("name"); // "kick"
let none = d.get("missing"); // NUL

Set a key-value pair. Mutates the dict and returns it for chaining.

let d = #{};
d.set("tempo", 120).set("beats", 4);

Remove a key and return its value, or NUL if the key was not present. Mutates the dict.

let d = #{"x": 1};
let x = d.remove("x"); // 1; d is now empty

Return the dict’s contents as arrays.

let d = #{"a": 1, "b": 2};
let ks = d.keys(); // #["a", "b"]
let vs = d.values(); // #[1, 2]
let es = d.entries(); // #[#["a", 1], #["b", 2]]

Return the number of entries.

let n = #{"a": 1, "b": 2}.length(); // 2

Check whether a key exists.

let yes = #{"a": 1}.contains_key("a"); // true

Return a new dict with both dicts’ entries; on key collisions, other’s value wins.

let defaults = #{"volume": 80, "muted": false};
let merged = defaults.merge(#{"volume": 100}); // volume: 100, muted: false

Apply fn(key, value) to each entry and return a new dict with the same keys and the transformed values.

let scores = #{"a": 1, "b": 2};
let scaled = scores.map(fn(k, v) { return v * 10; }); // #{"a": 10, "b": 20}

Keep only entries for which fn(key, value) returns a truthy value.

let d = #{"a": 1, "b": 5};
let big = d.filter(fn(k, v) { return v > 2; }); // #{"b": 5}

Fold the dict to a single value with fn(accumulator, key, value), starting from init.

let d = #{"a": 1, "b": 2};
let sum = d.reduce(fn(acc, k, v) { return acc + v; }, 0); // 3

Create an iterator over the keys.

let count = #{"a": 1, "b": 2}.iter().count(); // 2

Iterators come from .iter() on arrays, strings, dicts, and patterns. Lazy methods return a new iterator without consuming anything; eager methods drain the iterator and return a final value.

Lazy:

MethodReturnsMeaning
take(n)IteratorFirst n elements
skip(n)IteratorAll but the first n elements
step_by(n)IteratorEvery nth element (n > 0)
enumerate()Iterator[index, value] pairs
zip(other)IteratorPairs from two iterators, stops at the shorter
chain(other)IteratorThis iterator, then the other
map(fn)IteratorTransform each element
filter(fn)IteratorKeep elements passing a predicate

Eager:

MethodReturnsMeaning
next()ValueNext element, or NUL when exhausted
collect()ArrayRemaining elements as an array
count()NumberNumber of remaining elements
first()ValueFirst element, or NUL
last()ValueLast element, or NUL
find(fn)ValueFirst element passing the predicate, or NUL
any(fn)BooleanWhether any element passes
all(fn)BooleanWhether every element passes
fold(init, fn)ValueFold to a single value (init first!)
flatten()ArrayCollect, un-nesting one level of arrays

Slice the element stream lazily.

let nums = #[1, 2, 3, 4, 5, 6];
let a = nums.iter().take(3).collect(); // #[1, 2, 3]
let b = nums.iter().skip(4).collect(); // #[5, 6]
let c = nums.iter().step_by(2).collect(); // #[1, 3, 5]

Yield [index, value] pairs, counting from 0.

let pairs = #["kick", "snare"].iter().enumerate().collect();
// #[#[0, "kick"], #[1, "snare"]]

Combine two iterators — zip pairs elements (stopping at the shorter), chain appends one after the other.

let a = #[1, 2, 3].iter();
let b = #["x", "y"].iter();
let zipped = a.zip(b).collect(); // #[#[1, "x"], #[2, "y"]]
let joined = #[1, 2].iter().chain(#[3].iter()).collect(); // #[1, 2, 3]

Transform and select lazily — nothing runs until an eager method pulls elements.

let result = #[1, 2, 3, 4].iter()
.filter(fn(x) { return x % 2 == 0; })
.map(fn(x) { return x * x; })
.collect(); // #[4, 16]

Pull the next element, or NUL when the iterator is exhausted.

let it = #[1, 2].iter();
let a = it.next(); // 1
let b = it.next(); // 2
let c = it.next(); // NUL

Drain the iterator into an array, or just count the remaining elements.

let rest = #[1, 2, 3].iter().skip(1).collect(); // #[2, 3]
let n = #[1, 2, 3].iter().count(); // 3

Return a single element — NUL if there is none.

let nums = #[3, 7, 12];
let f = nums.iter().first(); // 3
let l = nums.iter().last(); // 12
let big = nums.iter().find(fn(x) { return x > 5; }); // 7

Test the elements against a predicate.

let nums = #[1, 2, 3];
let some = nums.iter().any(fn(x) { return x > 2; }); // true
let every = nums.iter().all(fn(x) { return x > 0; }); // true

Fold the remaining elements with fn(accumulator, element), starting from init. Note the initial value comes first — the reverse of array.reduce.

let sum = #[1, 2, 3].iter().fold(0, fn(acc, x) { return acc + x; }); // 6

Collect all elements, un-nesting one level of arrays. Non-array elements pass through unchanged.

let flat = #[#[1, 2], #[3], 4].iter().flatten(); // #[1, 2, 3, 4]