Standard Library — Types


Contents


Built-in Enums

The following enum types are defined by the prelude and are always available. A small set of variant constructors (Some, None, Ok, Err, Yielded, Done) are also available without qualification. All other prelude enum variants require qualification (e.g. Ordering.Less, IoError.Eof).

Ordering

enum Ordering
    Less
    Equal
    Greater
end

Represents the result of a comparison. Returned by the compare method of the Comparable[T] interface (see stdlib/interfaces.md). Ordering is a simple enum with no associated data.

Constructing:

result = 3.compare(5)          # Ordering.Less
result = 5.compare(5)          # Ordering.Equal
result = 7.compare(5)          # Ordering.Greater

Pattern matching:

match a.compare(b)
    Ordering.Less then println("a < b")
    Ordering.Equal then println("a == b")
    Ordering.Greater then println("a > b")
end

Interface conformance:

Interface Condition
PartialEq[Ordering] always (built-in enum equality)
Eq[Ordering] always (built-in enum equality)
Hash always (no associated data)
ToString always (enum auto-conformance)

Option[T]

enum Option[out T]
    Some(T)
    None
end

Represents an optional value. The ? suffix is syntactic sugar: int? means Option[int].

Constructing:

x: int? = Some(42)
y: int? = None

Destructuring:

match x
    Some(value) then println("got: {value}")
    None then println("nothing")
end

if x matches Some(value)
    println("got: {value}")
end

Propagation with ?: The ? operator unwraps Some(v) to v, or immediately returns None from the enclosing function. The enclosing function must return Option[U] for some U. See lang/semantics/values.md §2.12.3.

fn firstWord(text: str?) -> str?
    value = text?                   # unwrap or return None
    return Some(value.split(" ")[0])
end

Interface conformance:

Interface Condition
PartialEq[Option[T]] when T: PartialEq[T]
Eq[Option[T]] when T: Eq[T]
Hash when T: Hash
ToString when T: ToString

Result[T, E]

enum Result[out T, out E]
    Ok(T)
    Err(E)
end

Represents success or failure. Replaces exceptions for error handling.

Constructing:

success: Result[int, str] = Ok(42)
failure: Result[int, str] = Err("parse error")

result = int.parse("42")            # Ok(42)
result = int.parse("nope")          # Err("invalid character: n")

Destructuring:

match result
    Ok(value) then println("got: {value}")
    Err(e) then println("error: {e}")
end

Propagation with ?: The ? operator unwraps Ok(v) to v, or immediately returns Err(e) from the enclosing function. The enclosing function must return Result[U, E] where E matches the error type. See lang/semantics/values.md §2.12.3.

fn loadAndParse(path: str) -> Result[int, str]
    content = readFile(path)?      # unwrap Ok or return Err
    value = int.parse(content)?     # chain multiple fallible calls
    return Ok(value)
end

Interface conformance:

Interface Condition
PartialEq[Result[T, E]] when T: PartialEq[T] and E: PartialEq[E]
Eq[Result[T, E]] when T: Eq[T] and E: Eq[E]
Hash when T: Hash and E: Hash
ToString when T: ToString and E: ToString

GeneratorResult[Y, R]

enum GeneratorResult[out Y, out R]
    Yielded(Y)
    Done(R)
end

Returned by Generator[Y, R, N].next(). See lang/types.md §3.3.

Interface conformance:

Interface Condition
PartialEq[GeneratorResult[Y, R]] when Y: PartialEq[Y] and R: PartialEq[R]
Eq[GeneratorResult[Y, R]] when Y: Eq[Y] and R: Eq[R]
Hash when Y: Hash and R: Hash
ToString when Y: ToString and R: ToString

IoError

enum IoError
    Eof
    Other(str)
end

Represents I/O errors. Used by readln (in stdlib/functions.md) and the io module file functions (stdlib/io.md).

Variants:

Constructing:

err1 = IoError.Eof
err2 = IoError.Other("stdin closed")

Pattern matching:

match readln()
    Ok(line) then println("got: {line}")
    Err(IoError.Eof) then println("end of input")
    Err(IoError.Other(msg)) then println("I/O error: {msg}")
end

Interface conformance:

Interface Condition
PartialEq[IoError] always (built-in enum equality)
Eq[IoError] always (built-in enum equality)
Hash always (str is hashable)
ToString always (enum auto-conformance)

Generator[Y, R, N]

Generator[Y, R=(), N=never]

A built-in opaque type representing a suspended generator. Created by calling a gen fn function. Generator is not a struct or enum — it is a compiler-managed type with no user-accessible variants or fields.

Type parameters:

Common forms (via default type parameters):

Methods:

Interface conformance:

Interface Condition
Iterator[Y] when R = () and N = never
Iterable[Y] when R = () and N = never (returns self)

For full generator semantics, see lang/types.md §3.3.


Tuples

Tuple[out T1, out T2, ..., out Tn] is a built-in, fixed-size, heterogeneous, immutable type with 2 to 10 type parameters. All type parameters are covariant — tuples have no mutation methods, so covariance is sound. Tuples are part of the prelude and available without import.

Type syntax:

Tuple[int, str]                     # 2-tuple
Tuple[int, str, bool]               # 3-tuple

Literal syntax:

pair = (1, "hello")                  # Tuple[int, str]
triple = (true, 3.14, "yes")        # Tuple[bool, float, str]

Element access:

pair.0          # first element
pair.1          # second element

The index must be a compile-time integer literal within the tuple's arity.

Destructuring:

(x, y) = (10, 20)
(a, b, c) = triple

Interface conformance:

Interface Condition
PartialEq[Tuple[...]] when all element types satisfy PartialEq
Eq[Tuple[...]] when all element types satisfy Eq
Hash when all element types satisfy Hash
ToString when all element types satisfy ToString

Tuples do not satisfy Comparable or Iterable.


Built-in Collections

List[T]

An ordered, growable sequence of elements.

Construction:

items = [1, 2, 3, 4, 5]        # List[int]
names = ["alice", "bob"]        # List[str]
empty = []                      # List[never] — assignable to any List[T]
typed: List[int] = []           # explicit annotation required to mutate later

Bracket access:

first = items[0]                # int — returns the element
items[0] = 10                   # sets the element (requires mut)

Out-of-bounds access is a runtime error (panics). The index must be of type uint; only indices less than the list length are valid.

Methods:

Signature Description
fn length(self) -> uint Number of elements.
fn isEmpty(self) -> bool true if length is 0.
fn get(self, index: uint) -> Option[T] Return the element at index, or None if out of bounds.
fn push(mut self, value: T) Append an element to the end.
fn pop(mut self) -> Option[T] Remove and return the last element, or None if empty.
fn insert(mut self, index: uint, value: T) Insert at index, shifting subsequent elements right. Panics if index > length.
fn remove(mut self, index: uint) -> T Remove and return the element at index, shifting subsequent elements left. Panics if out of bounds.
fn first(self) -> Option[T] Return the first element, or None if empty.
fn last(self) -> Option[T] Return the last element, or None if empty.
fn contains(self, value: T) -> bool true if any element equals value. Requires T: PartialEq[T].
fn indexOf(self, value: T) -> Option[uint] Index of the first occurrence, or None. Requires T: PartialEq[T].
fn concat(self, other: List[T]) -> List[T] Return a new list with all elements of self followed by all elements of other.
fn reverse(self) -> List[T] Return a new list with elements in reverse order.
fn sort(self) -> List[T] Return a new sorted list. Requires T: Comparable[T]. Stable sort — preserves the relative order of elements that compare equal.
fn clear(mut self) Remove all elements, leaving the list empty.

List[T] also inherits the following methods from Iterable[T] (stdlib/interfaces.md): map, flatMap, filter, reduce, find, any, all, join, each, enumerate. The map, filter, flatMap, and enumerate methods return lazy Iterator values; use toList(), toMap(), or toSet() to materialize.

Concatenation:

combined = [1, 2].concat([3, 4])    # [1, 2, 3, 4]

List concatenation is an explicit method call. The + operator is not overloaded for lists — + works only on numeric types and str.

Iteration:

for item in items
    println("{item}")
end
for (i, item) in items.enumerate()
    println("{i}: {item}")
end

enumerate() is inherited from Iterable[T] (stdlib/interfaces.md) and returns Iterator[Tuple[uint, T]]. Tuple destructuring in the for loop binds the index and element.

The for loop calls iter(), which returns an Iterator[T]. Each element has type T.

Structurally modifying a list (e.g., push, pop, insert, remove) during iteration is a runtime panic. Element-level mutation via bracket assignment is safe (see types.md §3.4).

Interface conformance:

Interface Condition
PartialEq[List[T]] when T: PartialEq[T] — element-wise equality, same length
Eq[List[T]] when T: Eq[T]
Iterable[T] always — iter() iterates over elements
ToString when T: ToString — output format is implementation-defined and intended for diagnostic purposes only, not for serialization or user-facing display

Map[K, V]

A hash map from keys to values.

Key constraint: K must satisfy both Eq[K] and Hash. This is enforced at every point where Map[K, V] is written in a type expression. Type parameters used as map keys must declare the required constraints in their bounds. The compiler checks constraint satisfaction at declaration sites, not usage sites. The Eq requirement (rather than PartialEq) guarantees reflexive equality — a key always equals itself, which is necessary for reliable lookup and membership testing:

Map[str, int]       # ok — str implements Eq and Hash
Map[Point, str]     # ok — if Point implements Eq and Hash
Map[float, str]     # error — float does not implement Eq (NaN violates reflexivity)
Map[List[int], str] # error — List does not implement Hash

Construction:

m = {a: 1, b: 2, c: 3}        # Map[str, int]
empty = {}                      # Map[never, never] — assignable to any Map[K, V]
typed: Map[str, int] = {}       # explicit annotation also valid

Bare identifiers in map literals are always string keys. Empty strings are valid keys:

{"": 0}                         # Map[str, int] with empty string as key

Computed keys use bracket syntax:

{[42]: "hello"}                 # Map[int, str]
{[point]: "origin"}             # Map[Point, str] — if Point: Eq[Point] & Hash

Bracket access:

val = m["a"]                    # int — returns the value, panics if key missing
m["c"] = 3                     # sets the value (requires mut)

Map read access returns V directly. If the key is not present, it is a runtime error (panics). Use the .get() method for safe access that returns Option[V].

Methods:

Signature Description
fn length(self) -> uint Number of key-value entries.
fn isEmpty(self) -> bool true if length is 0.
fn has(self, key: K) -> bool true if the key exists.
fn get(self, key: K) -> Option[V] Return the value for key, or None if absent. Safe alternative to bracket access.
fn set(mut self, key: K, value: V) Insert or update the entry for key. Functionally equivalent to bracket assignment outside of iteration. During iteration, set is always a structural modification (panic) because the runtime cannot distinguish insert-vs-update without a key lookup — use bracket assignment m[key] = value to safely update existing keys during iteration.
fn delete(mut self, key: K) -> Option[V] Remove the entry for key and return the old value, or None if absent.
fn keys(self) -> List[K] Return a list of all keys. Order is not guaranteed.
fn values(self) -> List[V] Return a list of all values. Order matches keys().
fn entries(self) -> List[Tuple[K, V]] Return a list of key-value pairs as tuples.
fn mapValues[U](self, f: fn(V) -> U) -> Map[K, U] Return a new map with f applied to each value.
fn merge(self, other: Map[K, V]) -> Map[K, V] Return a new map containing all entries from both. If a key appears in both, other's value wins.
fn clear(mut self) Remove all entries, leaving the map empty.

Map[K, V] also inherits methods from Iterable[Tuple[K, V]] (stdlib/interfaces.md): map, flatMap, filter, reduce, find, any, all, join, each, enumerate. For these methods, the element type is Tuple[K, V]. The map, filter, flatMap, and enumerate methods return lazy Iterator values; use toList(), toMap(), or toSet() to materialize.

Iteration:

Maps iterate over key-value pairs as Tuple[K, V]:

Iteration order is unspecified. Programs must not depend on any particular order. Implementations are free to use insertion order, arbitrary order, or any other strategy.

for entry in m                  # entry: Tuple[str, int]
    println("{entry.0} = {entry.1}")
end

for (k, v) in m                 # destructured: k: str, v: int
    println("{k} = {v}")
end

for (_, v) in m                 # discard the key, bind only the value
    println("{v}")
end

The single-variable form binds each Tuple[K, V] directly. The tuple destructuring form (k, v) destructures each pair into its elements. The _ placeholder discards a binding (see lang/semantics/patterns.md §2.14).

Structurally modifying a map (e.g., set, delete, bracket insertion of new keys) during iteration is a runtime panic. Updating an existing key's value via bracket assignment is safe (see types.md §3.4).

Interface conformance:

Interface Condition
PartialEq[Map[K, V]] when K: PartialEq[K] and V: PartialEq[V] — same keys with equal values
Eq[Map[K, V]] when K: Eq[K] and V: Eq[V]
Iterable[Tuple[K, V]] always — iter() iterates over key-value pairs
ToString when K: ToString and V: ToString — output format is implementation-defined and intended for diagnostic purposes only, not for serialization or user-facing display

Set[T]

An unordered collection of unique values.

Key constraint: T must satisfy both Eq[T] and Hash. This is enforced at every point where Set[T] is written in a type expression. Note that Set requires Eq[T] (not just PartialEq[T]) because set membership relies on reflexive equality — an element must equal itself for membership tests to be consistent.

Construction:

There is no literal syntax for Set. Use the static from method:

s = Set.from([1, 2, 3, 2, 1])      # Set[int] — duplicates discarded
names = Set.from(["alice", "bob"])  # Set[str]
typed: Set[str] = Set.from([])      # explicit annotation required for empty set

Methods:

Signature Description
fn from(items: List[T]) -> Set[T] Static constructor. Duplicate elements in items are silently discarded.
fn length(self) -> uint Number of elements.
fn isEmpty(self) -> bool true if length is 0.
fn contains(self, value: T) -> bool true if value is a member of the set.
fn add(mut self, value: T) Add value to the set. No-op if already present.
fn remove(mut self, value: T) -> bool Remove value from the set. Returns true if the element was present, false if not.
fn union(self, other: Set[T]) -> Set[T] Return a new set containing all elements that appear in either set.
fn intersection(self, other: Set[T]) -> Set[T] Return a new set containing only elements that appear in both sets.
fn difference(self, other: Set[T]) -> Set[T] Return a new set containing elements in self that are not in other.
fn clear(mut self) Remove all elements, leaving the set empty.

Set[T] also inherits methods from Iterable[T] (stdlib/interfaces.md): map, flatMap, filter, reduce, find, any, all, join, each, enumerate. The map, filter, flatMap, and enumerate methods return lazy Iterator values; use toList(), toMap(), or toSet() to materialize.

Iteration:

Set[T] satisfies Iterable[T]. Iteration order is unspecified — programs must not depend on any particular order:

s = Set.from([3, 1, 2])
for item in s
    println("{item}")       # printed in unspecified order
end

Calling add, remove, or clear during iteration is a runtime panic.

Equality:

Two sets are equal if and only if they contain exactly the same elements (order is irrelevant):

Set.from([1, 2, 3]) == Set.from([3, 1, 2])    # true
Set.from([1, 2]) == Set.from([1, 2, 3])        # false

Interface conformance:

Interface Condition
PartialEq[Set[T]] always (T: Eq[T] & Hash is already required by Set)
Eq[Set[T]] always
Iterable[T] always — iter() iterates over elements
ToString when T: ToString — output format is implementation-defined and intended for diagnostic purposes only, not for serialization or user-facing display

Buffer

A mutable, growable sequence of raw bytes. Unlike str (which is immutable and always valid UTF-8) and List[byte] (which is a generic collection), Buffer is purpose-built for binary data manipulation — encoding, decoding, and binary file I/O.

Buffer is part of the prelude and available without import.

Construction:

buf = Buffer.new()                  # empty buffer
buf = Buffer.withCapacity(1024u)    # pre-allocate capacity (optimization hint)
buf = Buffer.from([0x48b, 0x69b])   # from a List[byte]

Buffer.withCapacity creates an empty buffer that has pre-allocated space for at least n bytes. The buffer's length is still 0 — capacity is an optimization hint to avoid repeated reallocations during sequential writes.

Bracket access:

val = buf[0]                # byte — returns the element at index
buf[0] = 0xFFb              # sets the element (requires mut)

Out-of-bounds access is a runtime error (panics). The index must be of type uint; only indices less than the buffer length are valid.

Methods:

Signature Description
fn new() -> Buffer Static constructor. Returns an empty buffer.
fn withCapacity(n: uint) -> Buffer Static constructor. Returns an empty buffer with pre-allocated capacity for at least n bytes.
fn from(bytes: List[byte]) -> Buffer Static constructor. Returns a buffer containing the given bytes.
fn length(self) -> uint Number of bytes.
fn isEmpty(self) -> bool true if length is 0.
fn get(self, index: uint) -> Option[byte] Return the byte at index, or None if out of bounds.
fn push(mut self, value: byte) Append a byte to the end.
fn pop(mut self) -> Option[byte] Remove and return the last byte, or None if empty.
fn clear(mut self) Remove all bytes, leaving the buffer empty.
fn slice(self, start: uint, end: uint) -> Buffer Return a new buffer containing bytes from start (inclusive) to end (exclusive). Panics if start > end or end > length.
fn concat(self, other: Buffer) -> Buffer Return a new buffer with all bytes of self followed by all bytes of other.
fn toList(self) -> List[byte] Return the contents as a List[byte].
fn toStr(self) -> Result[str, str] Validate contents as UTF-8 and return a str. Returns Err with a description if the bytes are not valid UTF-8.

Endian-aware write methods (append):

These methods encode an integer value into bytes and append them to the end of the buffer. LE = little-endian (least significant byte first), BE = big-endian (most significant byte first).

Signature Description
fn writeUint16LE(mut self, value: uint) Append value as 2 bytes, little-endian. Panics if value > 65535.
fn writeUint16BE(mut self, value: uint) Append value as 2 bytes, big-endian. Panics if value > 65535.
fn writeUint32LE(mut self, value: uint) Append value as 4 bytes, little-endian. Panics if value > 4294967295.
fn writeUint32BE(mut self, value: uint) Append value as 4 bytes, big-endian. Panics if value > 4294967295.
fn writeUint64LE(mut self, value: uint) Append value as 8 bytes, little-endian.
fn writeUint64BE(mut self, value: uint) Append value as 8 bytes, big-endian.
fn writeInt16LE(mut self, value: int) Append value as 2 bytes, little-endian. Panics if outside −32768..32767.
fn writeInt16BE(mut self, value: int) Append value as 2 bytes, big-endian. Panics if outside −32768..32767.
fn writeInt32LE(mut self, value: int) Append value as 4 bytes, little-endian. Panics if outside −2147483648..2147483647.
fn writeInt32BE(mut self, value: int) Append value as 4 bytes, big-endian. Panics if outside −2147483648..2147483647.
fn writeInt64LE(mut self, value: int) Append value as 8 bytes, little-endian.
fn writeInt64BE(mut self, value: int) Append value as 8 bytes, big-endian.

Endian-aware put methods (write at offset):

These methods encode an integer value and overwrite bytes at a specific offset in the buffer. The buffer must already contain enough bytes — put methods do not grow the buffer. Panics if offset + size > length (where size is 2, 4, or 8 bytes depending on the method). Value range checks are the same as the corresponding write methods.

Signature Description
fn putUint16LE(mut self, offset: uint, value: uint) Write value as 2 bytes at offset, little-endian.
fn putUint16BE(mut self, offset: uint, value: uint) Write value as 2 bytes at offset, big-endian.
fn putUint32LE(mut self, offset: uint, value: uint) Write value as 4 bytes at offset, little-endian.
fn putUint32BE(mut self, offset: uint, value: uint) Write value as 4 bytes at offset, big-endian.
fn putUint64LE(mut self, offset: uint, value: uint) Write value as 8 bytes at offset, little-endian.
fn putUint64BE(mut self, offset: uint, value: uint) Write value as 8 bytes at offset, big-endian.
fn putInt16LE(mut self, offset: uint, value: int) Write value as 2 bytes at offset, little-endian.
fn putInt16BE(mut self, offset: uint, value: int) Write value as 2 bytes at offset, big-endian.
fn putInt32LE(mut self, offset: uint, value: int) Write value as 4 bytes at offset, little-endian.
fn putInt32BE(mut self, offset: uint, value: int) Write value as 4 bytes at offset, big-endian.
fn putInt64LE(mut self, offset: uint, value: int) Write value as 8 bytes at offset, little-endian.
fn putInt64BE(mut self, offset: uint, value: int) Write value as 8 bytes at offset, big-endian.

Endian-aware read methods (read at offset):

These methods decode an integer value from bytes at a specific offset in the buffer. They do not modify the buffer. Panics if offset + size > length.

Signature Description
fn readUint16LE(self, offset: uint) -> uint Read 2 bytes at offset as a uint, little-endian.
fn readUint16BE(self, offset: uint) -> uint Read 2 bytes at offset as a uint, big-endian.
fn readUint32LE(self, offset: uint) -> uint Read 4 bytes at offset as a uint, little-endian.
fn readUint32BE(self, offset: uint) -> uint Read 4 bytes at offset as a uint, big-endian.
fn readUint64LE(self, offset: uint) -> uint Read 8 bytes at offset as a uint, little-endian.
fn readUint64BE(self, offset: uint) -> uint Read 8 bytes at offset as a uint, big-endian.
fn readInt16LE(self, offset: uint) -> int Read 2 bytes at offset as an int, little-endian (sign-extended).
fn readInt16BE(self, offset: uint) -> int Read 2 bytes at offset as an int, big-endian (sign-extended).
fn readInt32LE(self, offset: uint) -> int Read 4 bytes at offset as an int, little-endian (sign-extended).
fn readInt32BE(self, offset: uint) -> int Read 4 bytes at offset as an int, big-endian (sign-extended).
fn readInt64LE(self, offset: uint) -> int Read 8 bytes at offset as an int, little-endian.
fn readInt64BE(self, offset: uint) -> int Read 8 bytes at offset as an int, big-endian.

Buffer also inherits the following methods from Iterable[byte] (stdlib/interfaces.md): map, flatMap, filter, reduce, find, any, all, join, each, enumerate. The map, filter, flatMap, and enumerate methods return lazy Iterator values; use toList() to materialize.

Concatenation:

combined = buf1.concat(buf2)

Buffer concatenation is an explicit method call. The + operator is not overloaded for buffers.

Iteration:

for b in buf
    println("{b}")
end
for (i, b) in buf.enumerate()
    println("{i}: {b}")
end

The for loop calls iter(), which returns an Iterator[byte]. Each element has type byte.

Structurally modifying a buffer (e.g., push, pop, clear) during iteration is a runtime panic. Element-level mutation via bracket assignment is safe.

Interface conformance:

Interface Condition
PartialEq[Buffer] always — byte-wise equality, same length
Eq[Buffer] always
Iterable[byte] always — iter() iterates over bytes
ToString always — output format is implementation-defined and intended for diagnostic purposes only, not for serialization or user-facing display

Examples:

# Building a buffer manually
mut buf = Buffer.new()
buf.push(0x48b)      # 'H'
buf.push(0x69b)      # 'i'
buf.toStr()           # Ok("Hi")

# From a byte list
buf = Buffer.from([72b, 101b, 108b, 108b, 111b])
buf.toStr()           # Ok("Hello")
buf.length()          # 5u

# Bracket access
buf[0]                # 72b
buf.get(99u)          # None

# Slicing
header = buf.slice(0u, 4u)     # Buffer containing [72, 101, 108, 108]
header.length()                 # 4u

# Endian-aware encoding
mut out = Buffer.new()
out.writeUint32LE(0x01020304u)  # appends [0x04, 0x03, 0x02, 0x01]
out.writeUint16BE(256u)         # appends [0x01, 0x00]
out.length()                     # 6u

# Reading back
out.readUint32LE(0u)            # 0x01020304u
out.readUint16BE(4u)            # 256u

# Backpatching: write a placeholder, then fill it in later
mut code = Buffer.new()
code.writeUint32LE(0u)          # placeholder for jump offset at position 0
code.push(0x90b)                # some instruction bytes
code.push(0x90b)
code.putUint32LE(0u, 6u)       # backpatch: jump target is offset 6
code.readUint32LE(0u)           # 6u

Link copied to clipboard!