zena:core
import {…} from 'zena:core';
Foundational language types and utilities: strings, errors, options, results, boxes, ranges, arrays, iterators, and byte buffers.
Many declarations here are available automatically in the language prelude.
Evolving String Internals
String representation and low-level byte access methods are undergoing active redesign. See the Strings guide for current details.
Classes
String
final class String implements Hashable
An immutable sequence of characters with value equality.
Strings are created from string literals, template literals, concatenation,
slicing, and StringBuilder.
Evolving String Internals
Low-level byte operations like getByteAt and sliceBytes are temporary
implementation details that will become private or restricted in future
releases. See the Strings guide for current details.
new(data: ByteArray, start: i32, end: i32, encoding: Encoding)
Create a new array from a ByteArray.
Important: This should be considered a private constructor - strings are created by the compiler for literals or by StringBuilder/transcoding functions. When Zena supports private constructors, this will be marked as private to prevent misuse.
encoding: Encoding { get; }
The encoding of the backing bytes. Encoding is deliberately an internal detail almost everywhere; this exists for boundary code — canonical-ABI marshaling, host interop — that copies the bytes verbatim and must assert what it is copying rather than assume.
length: i32 { get; }
The length of the string in bytes.
Since string operations like slicing and getByteAt operate on bytes,
this consistently returns the byte capacity of the view regardless of
the underlying encoding (WTF-8 or WTF-16).
TODO: Rename to byteLength or remove entirely.
static fromByteArray(data: ByteArray, start: i32, end: i32, encoding: Encoding): String
Creates a String from a ByteArray with given bounds and encoding. This is used by StringBuilder and other internal operations.
static fromBytes(bytes: FixedArray<u8>): String
A String over a copy of bytes, taken as WTF-8 — the bytes a
response body or a file arrives as. Zena strings are views over
byte arrays, so nothing is re-encoded; the copy is what turns a
fixed array into the backing store a view needs.
toBytes(): FixedArray<u8>
A copy of this string's bytes, as they are stored — WTF-8 for
every string a component builds. The inverse of fromBytes: what
a list<u8> position wants, a header value or a body.
static fromParts<A extends Array<String>>(parts: A): String
Creates a new string by concatenating all parts. This is more efficient than chained + operators for multiple strings, as it allocates the result array only once.
Generic over the array representation: element access resolves through the bound, so each specialization reads its receiver directly with no interface dispatch.
static fromRawParts(parts: array<String>): String
The raw-array entry the compiler's synthesized string joins call —
template-literal lowering resolves it by name against a
one-parameter raw-array signature. Its body is also the call site
that keeps the FixedArray
getByteAt(index: i32): i32
Returns the byte at the given index.
For WTF-8 strings, this is the UTF-8 byte at the given index. For WTF-16 strings, this is the byte (not code unit) at the given index.
sliceBytes(start: i32, end: i32): String
O(1) zero-copy slice by byte indices. Returns a new String sharing the backing array.
SAFETY: Indices are byte offsets, not code point offsets. Slicing in the middle of a multi-byte UTF-8 sequence produces an invalid string. Use StringReader.mark() to get safe positions.
copy(): String
Force a copy - creates a new String with its own backing array. Use this when you need to release the parent string's memory.
copyBytesTo(target: ByteArray, targetOffset: i32, start: i32 = 0, length: i32 = -1): void
Copies bytes from this string to a target ByteArray.
startsWith(prefix: String): boolean
Returns true if this string starts with the given prefix.
endsWith(suffix: String): boolean
Returns true if this string ends with the given suffix.
split(separator: String): FixedArray<String>
Splits the string into an array of substrings separated by the given separator.
contains(needle: String): boolean
getBackingArrayIfFull(): ByteArray | null
Returns the backing ByteArray if the String spans the entire array (i.e. not a slice). Used for optimization by StringBuilder.
compareTo(other: String): i32
Compares this string with other in lexicographical code point order.
Returns:
< 0ifthis < other0ifthis == other> 0ifthis > other
In UTF-8, lexicographical byte comparison matches Unicode code point order.
asciiLowerCase(): String
FNV-1a hash of the string's bytes, cached in #hashCode after the first computation. Delegates to the compiler's shared string hash helper (generateStringHashFunction), which reads and updates the cache field.
Returns this string with every ASCII letter lowercased; every other byte
is left alone. See asciiLowerByte for why this is deliberately not
Unicode-aware.
Byte-wise, which is exactly right for WTF-8: an ASCII byte is always a
whole code point, and no continuation byte falls in 0x41..0x5A, so no
multi-byte sequence can be corrupted. A WTF-16 string would instead need
code-unit stepping — and that branch belongs here, on #encoding, rather
than in every caller, which is the reason this lives on String at all. No
String method supports WTF-16 yet and nothing constructs one today.
Returns this when there is nothing to change, so the common
already-lowercase case does not allocate.
asciiUpperCase(): String
Returns this string with every ASCII letter uppercased.
hashCode(): i32
Error
class Error
LookupError
class LookupError extends Error
Base class for errors when a lookup operation fails.
new(message: String)
IndexOutOfBoundsError
class IndexOutOfBoundsError extends LookupError
Thrown when an array/sequence index is out of bounds.
KeyNotFoundError
class KeyNotFoundError extends LookupError
Thrown when a key is not found in a map or similar collection.
new()
AsyncInSyncIteration
class AsyncInSyncIteration extends Error
Thrown by a synchronous for when an iterator's next() reports a
Pending step — a value that is not available synchronously. Consume
such an iterator with for await instead (docs/design/async-iteration.md).
new()
Some
class Some<T>
Represents a present value.
None
class None
Represents an absent value.
Outcome
sealed class Outcome<T, E>
Result's storable counterpart: the same two arms as a sealed
reference type, for when a result must outlive return position —
an array of per-item outcomes, a field holding a deferred answer.
Ok carries what the inline form's true arm does, Err the
false arm's payload.
Reach for the inline Result<T, E> first: it allocates nothing.
This type is for the positions inline tuples cannot occupy, and
costs one allocation per outcome.
Ok
Err
final class Err<T, E>(error: E) extends Outcome<T, E>
An error outcome.
error: E
Box
BoundedRange
final class BoundedRange
A bounded range with both start and end (exclusive). Represents [start, end) - includes start, excludes end.
FromRange
final class FromRange
A range from a start point to infinity/length. Represents [start, ∞) or [start, length).
ToRange
final class ToRange
A range from zero to an end point (exclusive). Represents [0, end).
FullRange
final class FullRange
A full range representing all elements. Represents [0, length).
Resource
abstract resource class Resource implements OwnState
The classes declared with the resource modifier — those holding
something the garbage collector cannot reclaim, and therefore carrying a
release obligation.
resource class R satisfies this without an implements clause, the way
a case class satisfies Hashable. It exists to be bounded against:
disown and adopt take a T extends Resource rather than any T,
because moving a value between the affine and manually-managed regimes is
only meaningful for something that has a disposal obligation to move.
There is deliberately no way to hold one. Nothing is assignable to
Resource, because the only reference to a resource a program can name is
a handle — Own<R>, Borrow<R>, Unmanaged<R> — and those are nominally
distinct types that assign to no supertype at all. That is what keeps a
widening conversion from dropping the permissions the handle carries, and
it is a property of the handles rather than of this interface: the same
applies to every supertype, Resource or otherwise.
Resource-ness is what this names; affineness is what Own<T> declares.
They are separate properties — see ownership.md §"Resource-ness and
affineness are different properties" — so this bound restricts neither
Own<T> itself nor the ordinary classes it will eventually wrap.
The root of every resource hierarchy. A resource class with no
explicit superclass extends this one, so the lifecycle flag has an
ordinary in-language home rather than a field codegen injects, and
disown/adopt read it with an ordinary method call rather than an
intrinsic. Being the root is also what makes ownership.md
§"Resource-ness is inherited" cheap to satisfy: single inheritance means
a resource cannot also extend an ordinary class, so the rule that its
chain is all resources is structural rather than a check.
Declaring no method that returns this is load-bearing, not incidental.
That is the whole reason an arbitrary superclass is unsafe for a resource
and this one is not.
ResourceStateError
class ResourceStateError extends Error
Raised when a regime change is handed a resource in the wrong state: adopting one that was never disowned, or disowning one twice.
Both are programming errors rather than expected conditions — the
resource was already claimed by someone else, or already released — so
they throw rather than returning a Result. A tryAdopt handing back
the branch can be added if a caller ever wants it.
new(operation: String, actual: String, expected: String)
TemplateStringsArray
final class TemplateStringsArray
Represents the string literals in a tagged template expression.
When you write tag\Hello ${name}!`` the tag function receives:
- strings: TemplateStringsArray containing ["Hello ", "!"]
- values: array containing [name]
Key properties:
- Immutable: The strings array cannot be modified.
- Cached: The same TemplateStringsArray instance is reused across invocations of the same tagged template expression.
- Interleaved: Strings and values alternate, with strings on both ends.
For
\a${x}b${y}c`: strings = ["a", "b", "c"], values = [x, y]. This means there is alwaysstrings.length == values.length + 1`.
The raw property provides access to the raw (unescaped) string literals,
where escape sequences like \\n appear as two characters (\ and n)
rather than being interpreted as a newline.
Example:
let tag = (strings: TemplateStringsArray, values: FixedArray<anyref>): String => {
// strings[0] = "Line 1\nLine 2" (newline interpreted)
// strings.raw[0] = "Line 1\\nLine 2" (backslash-n literal)
return strings[0];
};
tag\`Line 1\\nLine 2\`;
FilterIterator
final class FilterIterator<T> implements Iterator<T>
An Iterator that yields elements matching a predicate.
FilteredIterable
final class FilteredIterable<T> implements Iterable<T>
An Iterable that lazily filters elements using a predicate.
new(source: Iterable<T>, predicate: (item: T) => boolean)
filter(predicate: (item: T) => boolean): Iterable<T>
contains(value: T): boolean
all(predicate: (item: T) => boolean): boolean
some(predicate: (item: T) => boolean): boolean
fold<R>(initial: R, combine: (acc: R, item: T) => R): R
find(predicate: (item: T) => boolean): inline (true, T) | inline (false, _)
ArrayIterator
final class ArrayIterator<T> implements Iterator<T>
Iterator over a FixedArray
GrowableArrayIterator
final class GrowableArrayIterator<T> implements Iterator<T>
FixedArray
final extension class FixedArray<T> with IterableUtils<T> implements MutableArray<T>, Iterable<T>
new(length: i32, value: T)
length: i32
static from<A>(seq: Array<A>): FixedArray<A>
map<U>(f: (item: T, index: i32, seq: FixedArray<T>) => U): FixedArray<U>
fold<R>(initial: R, combine: (acc: R, item: T) => R): R
reverse(): FixedArray<T>
slice(start: i32, end: i32): FixedArray<T>
Returns a shallow copy of a portion of the array. The start index is inclusive, the end index is exclusive. Negative indices are not supported.
operator [](index: i32): T
operator []=(index: i32, value: T): void
operator [](r: BoundedRange): FixedArray<T>
Returns a slice of the array using BoundedRange (a..b).
operator [](r: FromRange): FixedArray<T>
Returns a slice from start to end of array (a..).
operator [](r: ToRange): FixedArray<T>
Returns a slice from beginning to end index (..b).
operator [](_r: FullRange): FixedArray<T>
Returns a copy of the entire array (..).
7 inherited members
Array
Iterable
contains(value: T): boolean
Returns true if the collection contains the specified value.
all(predicate: (item: T) => boolean): boolean
Returns true if all elements match the given predicate. Returns true if the collection is empty.
some(predicate: (item: T) => boolean): boolean
Returns true if at least one element matches the given predicate. Returns false if the collection is empty.
find(predicate: (item: T) => boolean): inline (true, T) | inline (false, _)
Finds the first element that matches the predicate. Returns an inline tuple (true, item) if found, or (false, _) if not.
filter(predicate: (item: T) => boolean): Iterable<T>
Returns a new iterable containing only the elements that match the predicate.
GrowableArray
final class GrowableArray<T> implements MutableArray<T>, Iterable<T>
new(capacity: i32 = 8, adopting: FixedArray<T> | null = null)
adopting, when given, becomes the backing storage, full: length
starts at its length, no copy is made, and capacity is ignored.
The caller must hand the buffer over — writes through either alias
would be visible through the other. from copies; this adopts.
(A constructor parameter rather than a static so it works from
generic bodies — see the issue linked from growable below.)
length: i32 { get; }
static from<A>(seq: Array<A>): GrowableArray<A>
push(value: T): void
pop(): T
map<U>(f: (item: T, index: i32, seq: GrowableArray<T>) => U): GrowableArray<U>
contains(value: T): boolean
all(predicate: (item: T) => boolean): boolean
some(predicate: (item: T) => boolean): boolean
fold<R>(initial: R, combine: (acc: R, item: T) => R): R
find(predicate: (item: T) => boolean): inline (true, T) | inline (false, _)
filter(predicate: (item: T) => boolean): Iterable<T>
ImmutableArray
final extension class ImmutableArray<T> with IterableUtils<T> implements Array<T>
length: i32
map<U>(f: (item: T, index: i32, seq: ImmutableArray<T>) => U): FixedArray<U>
operator [](index: i32): T
9 inherited members
Array
new(capacity: i32 = 8): GrowableArray<T>
An empty growable array, with room for capacity items before it grows.
new growable(capacity: i32 = 8): GrowableArray<T>
The same as new Array(), named for symmetry with fixed.
new fixed(length: i32, value: T): FixedArray<T>
A fixed-length array of length copies of value.
Iterable
contains(value: T): boolean
Returns true if the collection contains the specified value.
all(predicate: (item: T) => boolean): boolean
Returns true if all elements match the given predicate. Returns true if the collection is empty.
some(predicate: (item: T) => boolean): boolean
Returns true if at least one element matches the given predicate. Returns false if the collection is empty.
fold<R>(initial: R, combine: (acc: R, item: T) => R): R
Reduces the collection to a single value by accumulating state.
find(predicate: (item: T) => boolean): inline (true, T) | inline (false, _)
Finds the first element that matches the predicate. Returns an inline tuple (true, item) if found, or (false, _) if not.
filter(predicate: (item: T) => boolean): Iterable<T>
Returns a new iterable containing only the elements that match the predicate.
ByteBuffer
final class ByteBuffer
A growable buffer for efficiently constructing binary data.
new(capacity: i32 = 256)
Creates a new ByteBuffer with the specified initial chunk capacity.
writeByte(b: i32): ByteBuffer
Write a single byte.
writeBytes(src: ByteArray): ByteBuffer
Write all bytes from a ByteArray.
writeBytesSlice(src: ByteArray, offset: i32, len: i32): ByteBuffer
Write a slice of bytes from a ByteArray.
writeBufferRange(other: ByteBuffer, start: i32, end: i32): ByteBuffer
Write the byte range [start, end) of another ByteBuffer's contents.
writeBuffer(other: ByteBuffer): ByteBuffer
Write another ByteBuffer's contents.
writeU16(value: i32): ByteBuffer
Write a 16-bit unsigned integer (little-endian).
writeU32(value: i32): ByteBuffer
Write a 32-bit unsigned integer (little-endian).
writeU64(value: i64): ByteBuffer
Write a 64-bit integer (little-endian).
writeF32(value: f32): ByteBuffer
Write a 32-bit float (little-endian).
writeF64(value: f64): ByteBuffer
Write a 64-bit float (little-endian).
writeULEB128(value: i32): ByteBuffer
Write an unsigned LEB128 encoded integer.
writeSLEB128(value: i32): ByteBuffer
Write a signed LEB128 encoded integer.
writeULEB128_64(value: i64): ByteBuffer
Write a 64-bit unsigned LEB128.
writeSLEB128_64(value: i64): ByteBuffer
Write a 64-bit signed LEB128.
getByte(index: i32): i32
Read a byte at an absolute position.
setByte(index: i32, value: i32): void
Write a byte at an absolute position (for patching).
patchU32(index: i32, value: i32): void
Patch a U32 at an absolute position (little-endian).
toByteArray(): ByteArray
Convert to a single contiguous ByteArray.
clear(): void
Clear the buffer, reusing the first chunk.
StringBuilder
final class StringBuilder
An append-only sequence of characters for efficient string construction.
new(capacity: i32 = 16)
Creates a new StringBuilder with the specified initial capacity.
append(s: String): StringBuilder
Appends a string to the builder.
appendByte(b: i32): StringBuilder
Appends a single byte.
appendI32(n: i32): StringBuilder
Appends a signed 32-bit integer as decimal. Uses shared conversion from zena:string-convert.
appendU32(n: u32): StringBuilder
Appends an unsigned 32-bit integer as decimal. Uses shared conversion from zena:string-convert.
appendI64(n: i64): StringBuilder
Appends a signed 64-bit integer as decimal. Uses shared conversion from zena:string-convert.
appendU64(n: u64): StringBuilder
Appends an unsigned 64-bit integer as decimal. Uses shared conversion from zena:string-convert.
appendF32(n: f32): StringBuilder
Appends a 32-bit float as decimal. Uses shared conversion from zena:string-convert.
appendF64(n: f64): StringBuilder
Appends a 64-bit float as decimal. Uses shared conversion from zena:string-convert.
toString(): String
Returns the string content.
clear(): void
Clears the builder.
static fromString(s: String): StringBuilder
Creates a new StringBuilder initialized with the content of the given String. If the string uses its entire backing ByteArray, the builder will adopt the array directly (zero-copy).
IterableJoin
extension class IterableJoin<T>
join(stringify: (item: T) => String, separator: String = ', '): String
StringReader
final class StringReader
StringReader - A cursor-based string parser that tracks byte positions.
Use StringReader for parsing to ensure slices are always at valid UTF-8 code point boundaries. Positions from mark() are safe to use with sliceBytes().
Example usage:
let r = new StringReader(input);
r.skipWhitespace();
let start = r.mark();
while (!r.isAtEnd && r.peekByte() != 34) { // 34 = '"'
r.advance(); // Move by code point (safe for Unicode)
}
let token = r.sliceFrom(start); // Safe: positions from mark()
new(source: String)
peekByte(): i32
Peek at the current byte without consuming it. Returns -1 if at end of string. Use for ASCII characters (< 128) like delimiters: { } [ ] , : " etc.
peekByteAt(offset: i32): i32
Peek at a byte at offset from current position. Returns -1 if out of bounds.
advanceByte(): i32
Consume and return the current byte. Returns -1 if at end. WARNING: Only use for ASCII (< 128). For Unicode text, use advance().
peek(): i32
Peek at the current Unicode code point without consuming it. Returns -1 if at end of string. Decodes UTF-8 sequences properly.
TODO: Use shift operators (<< 6, << 12, << 18) when available in Zena. Currently using multiplication as a workaround.
advance(): i32
Consume and return the current Unicode code point. Returns -1 if at end. Advances by the correct number of bytes (1-4 for UTF-8).
TODO: Use shift operators (<< 6, << 12, << 18) when available in Zena. Currently using multiplication as a workaround.
skip(count: i32): void
Skip N code points (not bytes). Properly handles multi-byte UTF-8 sequences.
mark(): i32
Mark the current position for later slicing. Returns an opaque position value safe to use with sliceFrom/sliceRange.
reset(pos: i32): void
Reset to a previously marked position.
sliceFrom(start: i32): String
Extract a slice from a marked position to current position. This is SAFE because both positions are at code point boundaries.
sliceRange(start: i32, end: i32): String
Extract a slice between two marked positions. This is SAFE because both positions are at code point boundaries.
matchByte(expected: i32): boolean
Check if current byte matches, and advance if it does. Returns true if matched.
skipWhitespace(): void
Skip ASCII whitespace (space, tab, newline, carriage return).
skipBytes(n: i32): void
Skip N bytes (not code points). Use with caution - only when you know the bytes are at valid boundaries.
skipBytesWhile(predicate: (byte: i32) => boolean): void
Skip bytes while predicate returns true. Predicate receives byte value (not code point). Useful for ASCII character classes: digits, letters, etc.
readUntil(needle: String): String | null
Reads a substring up to the next occurrence of needle.
If needle is found, returns the substring before it and advances the cursor past needle.
If needle is not found, returns null and does not advance the cursor.
readToEnd(): String
Reads the remaining substring from current position to the end of the string, advancing the cursor to the end.
Interfaces
Hashable
interface Hashable
A type that can be used as a key in hash-based collections like HashMap and HashSet.
The contract: if two values are equal (per ==, which is a virtual call to
operator == when the class defines one, and reference equality otherwise),
they must return the same hashCode. The reverse is not required — unequal
values may share a hash code, though fewer collisions means better
performance.
A class that uses reference equality (no operator ==) should return an
identity hash: a value that is unique per instance and stable for the
lifetime of the instance, e.g. a counter assigned in the constructor.
Case classes automatically satisfy Hashable: the compiler generates a
structural hashCode() (and operator ==) from their parameters.
Primitives (i32, boolean, enums, and distinct types over them) also satisfy
the Hashable constraint; they hash to their own value. Strings hash with
FNV-1a, cached after the first computation.
hashCode(): i32
Disposable
interface Disposable
A value holding something the garbage collector cannot reclaim.
dispose is symbol-keyed rather than name-keyed. dispose is a common
enough method name that a class may already have one meaning something
unrelated, and this is a protocol the language itself invokes implicitly —
a silent collision would be a resource released at the wrong time. Call it
as value.[Disposable.dispose]().
import { Disposable } from './ownership.zena';
class Descriptor implements Disposable {
#handle: i32;
new(this.#handle);
[Disposable.dispose](): void { descriptorDrop(this.#handle); }
}
Disposal must be idempotent: dispose() may be called on an already
disposed value and must not release the underlying resource twice. Until
affine checking lands in O2, nothing statically prevents a double call, and
a resource wrapper's state flag is what makes the second one a no-op.
Iterator
interface Iterator<T>
A stateful iterator over elements of type T.
Usage pattern with let-pattern conditions:
let iter = collection.[Iterable.iterator]();
while (let (true, item) = iter.next()) {
// use item
}
Design: Uses inline tuple returns for zero-allocation iteration.
- next() returns inline (true, T) when there's an element
- next() returns inline (false, _) when exhausted
next(): inline (true, T) | inline (false, _)
Advances the iterator and returns the next element.
Returns inline (true, element) if there is an element,
inline (false, _) if exhausted.
Iterable
interface Iterable<T>
A collection that can produce an Iterator.
contains(value: T): boolean
Returns true if the collection contains the specified value.
all(predicate: (item: T) => boolean): boolean
Returns true if all elements match the given predicate. Returns true if the collection is empty.
some(predicate: (item: T) => boolean): boolean
Returns true if at least one element matches the given predicate. Returns false if the collection is empty.
fold<R>(initial: R, combine: (acc: R, item: T) => R): R
Reduces the collection to a single value by accumulating state.
find(predicate: (item: T) => boolean): inline (true, T) | inline (false, _)
Finds the first element that matches the predicate. Returns an inline tuple (true, item) if found, or (false, _) if not.
filter(predicate: (item: T) => boolean): Iterable<T>
Returns a new iterable containing only the elements that match the predicate.
Array
interface Array<T> extends Iterable<T>
A sequence with a length and indexed reads.
This is the read-only view; MutableArray adds indexed writes. The
constructors build the standard implementations — new Array() is a
GrowableArray and new Array.fixed(n, value) a FixedArray — and are
typed as those classes, so the result has the class's full API.
(The modules declaring those classes import this one back; classes and
interfaces may cross an import cycle.)
new(capacity: i32 = 8): GrowableArray<T>
An empty growable array, with room for capacity items before it grows.
new growable(capacity: i32 = 8): GrowableArray<T>
The same as new Array(), named for symmetry with fixed.
new fixed(length: i32, value: T): FixedArray<T>
A fixed-length array of length copies of value.
length: i32 { get; }
map<U>(f: (item: T, index: i32, seq: this) => U): Array<U>
operator [](index: i32): T
6 inherited members
Iterable
contains(value: T): boolean
Returns true if the collection contains the specified value.
all(predicate: (item: T) => boolean): boolean
Returns true if all elements match the given predicate. Returns true if the collection is empty.
some(predicate: (item: T) => boolean): boolean
Returns true if at least one element matches the given predicate. Returns false if the collection is empty.
fold<R>(initial: R, combine: (acc: R, item: T) => R): R
Reduces the collection to a single value by accumulating state.
find(predicate: (item: T) => boolean): inline (true, T) | inline (false, _)
Finds the first element that matches the predicate. Returns an inline tuple (true, item) if found, or (false, _) if not.
filter(predicate: (item: T) => boolean): Iterable<T>
Returns a new iterable containing only the elements that match the predicate.
MutableArray
interface MutableArray<T> extends Array<T>
operator []=(index: i32, value: T): void
12 inherited members
Array
new(capacity: i32 = 8): GrowableArray<T>
An empty growable array, with room for capacity items before it grows.
new growable(capacity: i32 = 8): GrowableArray<T>
The same as new Array(), named for symmetry with fixed.
new fixed(length: i32, value: T): FixedArray<T>
A fixed-length array of length copies of value.
length: i32 { get; }
map<U>(f: (item: T, index: i32, seq: this) => U): Array<U>
operator [](index: i32): T
Iterable
contains(value: T): boolean
Returns true if the collection contains the specified value.
all(predicate: (item: T) => boolean): boolean
Returns true if all elements match the given predicate. Returns true if the collection is empty.
some(predicate: (item: T) => boolean): boolean
Returns true if at least one element matches the given predicate. Returns false if the collection is empty.
fold<R>(initial: R, combine: (acc: R, item: T) => R): R
Reduces the collection to a single value by accumulating state.
find(predicate: (item: T) => boolean): inline (true, T) | inline (false, _)
Finds the first element that matches the predicate. Returns an inline tuple (true, item) if found, or (false, _) if not.
filter(predicate: (item: T) => boolean): Iterable<T>
Returns a new iterable containing only the elements that match the predicate.
Mixins
IterableUtils
mixin IterableUtils<T> on Iterable<T>
Common iteration utilities for any collection that implements Iterable
filter(predicate: (item: T) => boolean): Iterable<T>
Returns a new iterable containing only the elements that match the predicate.
contains(value: T): boolean
Checks if the collection contains the specified value using equality.
all(predicate: (item: T) => boolean): boolean
Returns true if all elements match the given predicate. Returns true if the collection is empty.
some(predicate: (item: T) => boolean): boolean
Returns true if at least one element matches the given predicate. Returns false if the collection is empty.
fold<R>(initial: R, combine: (acc: R, item: T) => R): R
Reduces the collection to a single value by accumulating state.
find(predicate: (item: T) => boolean): inline (true, T) | inline (false, _)
Finds the first element that matches the predicate. Returns an inline tuple (true, item) if found, or (false, _) if not.
Enums
Encoding
Type aliases
StackRef
type StackRef = anyref
Opaque host-side stack-trace handle.
Option
type Option<T> = Some<T> | None
A value that is either present (Some) or absent (None).
Result
type Result<T, E> = inline (true, T, _) | inline (false, _, E)
Result<T, E> — a value that is either an ok payload or an error payload.
Unlike Option
There are three lanes, not two, so that each lane is type-homogeneous: the
discriminant, the ok payload, and the error payload. The unused payload in
each arm is a hole (_).
import { Result } from './result.zena';
let checkedSub = (a: i32, b: i32): Result<i32, String> => {
if (b > a) {
return (false, _, "underflow");
}
return (true, a - b, _);
};
if (let (true, v, _) = checkedSub(10, 4)) {
// v is 6
}
Inline tuples are return-position-only, so Result<T, E> may name a
function's return type but cannot be stored in a variable, field, or
collection. To keep a result around, store it as an Outcome below,
or match it and store the payload.
Range
type Range = BoundedRange | FromRange | ToRange | FullRange
Union type of all range types.
Own
type Own<T> = T
The owning reference to a resource. Affine: it may be moved, returned or stored, but not duplicated, and it is released when it leaves scope unmoved.
Erased at codegen: a distinct type is nominally distinct for checking but
shares its target's representation, so a handle costs no allocation and no
indirection. The three differ in permissions, not data.
Own<T> is what declares affineness; resource class declares
resource-ness. The two are separate on purpose — see ownership.md
§"Resource-ness and affineness are different properties" — which is why
Own<T> will also apply to ordinary classes (from a provably exclusive
source) once layer 4 needs it.
Borrow
type Borrow<T> = T
A borrowed reference. Freely copied, but second-class: it may not be returned (except derived from a single borrow parameter), stored in a field or array, or captured by a closure. Never releases what it points at.
At unrestricted instantiations Borrow<T> is the identity — Borrow<i32>
is i32 — which is what keeps container APIs from forking.
Unmanaged
type Unmanaged<T> = T
A disowned resource: aliasable like an ordinary reference, and never
implicitly dropped. You must dispose() it yourself or adopt() it back
into the affine world.
This is the population using was designed for. Entering it costs
leak-freedom and compile-time use-after-dispose detection; it costs neither
type soundness nor memory safety, because the state flag turns a bad
adopt into a clean error rather than a double free.
Scoped
type Scoped<T> = T
A value that may not be duplicated and may not outlive the extent it
derives from — the corner of the universe table where both
restrictions bind (ownership.md §"ScopedBorrow across await returns
Scoped<Future<T>>, so the frame holding the borrow cannot escape
the borrow's extent.
A scoped value must be consumed exactly once on every path: a scoped
future by await or a move, a scoped iterator by for/in. It may
not be stored in a field, container, record, or tuple, and may not
be captured by a closure. A first-class Future<T> coerces to
Scoped<Future<T>>; there is no way back.
ScopedFrom
type ScopedFrom<T, R>
Scoped<R> when T carries a second-class type — a scoped value, a
restricted borrow, or a scoped type parameter — and R otherwise
(ownership.md §"Combinator audit", docs/design/type-operators.md).
For a generic whose result must be scoped exactly when an input is:
Future.allSettled<scoped T> returns
ScopedFrom<T, Future<Array<Outcome<Awaited<T>, Error>>>>, so a
call over scoped futures yields a scoped result the caller must
consume, and a call over ordinary futures yields the ordinary future
it always did. Inside the generic body the operator reads as
Scoped<R>; substitution collapses it once T is known.
TemplateTag
type TemplateTag<T> = (strings: TemplateStringsArray, values: FixedArray<anyref>) => T
Type alias for template tag functions.
Example usage:
const html: TemplateTag<String> = (strings, values) => {
// strings[0], strings[1], etc. are the literal parts
// values[0], values[1], etc. are the interpolated expressions
// strings.raw gives access to raw (unescaped) strings
return '...';
};
const result = html`<div>${name}</div>`;
Functions
asciiLowerByte
function asciiLowerByte(b: i32): i32
Returns the ASCII lowercase of b, leaving every other byte or code point
unchanged.
asciiUpperByte
function asciiUpperByte(b: i32): i32
Returns the ASCII uppercase of b.
unreachable
declare function unreachable(): never
captureStackTrace
declare function captureStackTrace(): StackRef | null
formatStackTrace
declare function formatStackTrace(stack: StackRef | null): String | null
some
function some<T>(value: T): Option<T>
Creates a Some
hash
declare function hash<T>(value: T): i32
The hash code of a value, dispatched on its static type: primitives hash by
value, Strings by content, and classes through hashCode().
This is what hash-based collections call. Prefer it over hashCode() — it
works for primitives, which have no methods.
equals
declare function equals<T>(a: T, b: T): boolean
Whether two values of the same type are equal, dispatched on their static
type: primitives compare by value, Strings by content, and classes through
operator == when the class defines one and by reference otherwise.
disown
function disown<T extends Resource>(value: Own<T>): Unmanaged<T>
Leaves the affine regime: takes the owning handle and hands back an aliasable one that is never implicitly dropped.
let raw: Unmanaged<Descriptor> = disown(f); // f is consumed
// … alias it, store it in a field, put it in an ordinary Array …
let f2: Own<Descriptor> = adopt(raw); // back under implicit drop
The handles are erased, so this re-types the object rather than allocating
anything: Own<T> and Unmanaged<T> lower to the same wasm type, and the
body compiles to an identity function returning its argument. The call
itself is not yet inlined away.
Requires the resource to be owned and leaves it disowned, so
disowning the same resource twice throws rather than handing out two
unmanaged aliases with one disposal obligation between them.
disown consumes its argument. Otherwise an Own that will implicitly
drop would coexist with an Unmanaged alias to the same resource, and the
drop would run under an alias that outlives it. Move checking enforces the
consumption for local bindings — a caller that keeps using the original
Own after disowning it gets a compile error, through the ordinary rule
that an argument to an Own parameter is a move. The flag still guards
the routes the checker does not track, such as an Own read out of a
field.
The cast is legal here and nowhere else: Own<T> and Unmanaged<T> are
opaque types declared in this file, and both erase to T, so re-labelling
one as the other mints nothing. See ownership.md §"Handles are not
forgeable" — outside this file the same cast is a diagnostic, which is what
makes disown and adopt the only doors between the two regimes.
adopt
function adopt<T extends Resource>(value: Unmanaged<T>): Own<T>
Re-enters the affine regime: takes a disowned handle back under implicit drop, so it is released at scope exit again.
The inverse of disown, and the only way back — Unmanaged<R>
has no user-callable dispose, since it is not an owner and cannot call the
consuming form. A disowned resource is either scoped with using or adopted
back.
Throws when the resource is not in the disowned state: owned
means something else adopted it first, so two racing adopters do not both
succeed — the loser gets a clear error rather than a second owner and,
once implicit drop lands, a double free.
dropped throws too: every consuming dispose sets it, so adopting an
already-released resource is reported as exactly that.
adopt cannot retract aliases. Aliasability is the point of the disowned
state, so the flag is the only defence here, by design.
map
function map<T, U>(it: Scoped<Iterator<T>>, f: (v: T) => U): Scoped<Iterator<U>>
The elements of a scoped iterator mapped through f. Driving the
result drives it, inside the same extent — the input is consumed
(a scoped iterator is driven exactly once), and the result derives
from the same borrow, so it is scoped too.
filter
function filter<T>(it: Scoped<Iterator<T>>, keep: (v: T) => boolean): Scoped<Iterator<T>>
The elements of a scoped iterator for which keep answers true.
take
function take<T>(it: Scoped<Iterator<T>>, count: i32): Scoped<Iterator<T>>
At most the first count elements of a scoped iterator. The loop is
always entered — every path out of this body must consume it — so
with count <= 0 one element is still pulled from the source before
the frame is disposed.
dedent
function dedent(strings: TemplateStringsArray, values: FixedArray<String>): String
A template tag that strips the source indentation from a multi-line template literal, so a block of text can be written at the indentation of the code around it and still come out flush left:
let usage = dedent`
zena build <entry>
-o <path> where to write the module
`;
// "zena build <entry>\n -o <path> where to write the module"
The rules:
- The opening line is dropped, along with its line break, when it is blank — which it is in the form above, where the content starts on the line after the backtick. The closing line is dropped the same way. Neither is dropped when it has content on it: a line with content is a line, and it takes part in the rule below like any other.
- The indentation removed from every remaining line is the longest common prefix of the indentation of the lines that have content on them. It is compared byte for byte, so a file that mixes tabs and spaces dedents by what its lines actually share rather than by a count that assumes a tab width.
- Blank lines are ignored when computing that prefix and come out empty, so a line of leftover trailing spaces is not what decides the result.
- Interpolated values are inserted verbatim. A value containing newlines is not re-indented, and its newlines do not start lines that participate in the computation: only indentation written in the literal counts. A line that starts with a value is a content line, and the whitespace before the value is its indentation.
Interpolations must be Strings — a tag receives its values without the
conversion ${} performs in an untagged template literal, so write
dedent`count: ${`${n}`}` (or i32ToString(n)) for a number.
The text dedented is the cooked strings, so an escape is processed before
the indentation is measured: a \n written as an escape breaks a line here
exactly as a real newline does. Today that is theory rather than practice —
a tagged template whose literal contains any escape sequence fails to
compile, whatever the tag (see elematic/zena#84).
fixed
function fixed<T>(items: FixedArray<T>): FixedArray<T>
Its argument, unchanged: fixed([1, 2, 3]).
The parameter supplies the mutable context, so a literal argument builds directly as a FixedArray — a mutable literal in expression position, where no annotation can say so. The identity body inlines away, leaving the bare allocation.
growable
function growable<T>(items: FixedArray<T>): GrowableArray<T>
A growable array from an array literal: growable([1, 2, 3]).
The parameter supplies the mutable context, so the literal builds
directly as a FixedArray and is adopted as the backing storage —
no copy, unlike GrowableArray.from over an interface-typed source.
Until literal decorators land, this is the concise spelling for a
growable array with initial contents.
newByteArray
function newByteArray(size: i32): ByteArray
Create a new ByteArray of the specified size, filled with zeros.
copyBytes
function copyBytes(dest: ByteArray, destOffset: i32, src: ByteArray, srcOffset: i32, length: i32): void
Copy bytes from source to destination.
writeI32
function writeI32(value: i32, buffer: ByteArray, bufferEnd: i32): inline (i32, i32)
Writes an i32 to a buffer. Buffer must have at least 11 bytes. Returns (startPos, endPos) where the digits are in buffer[startPos..endPos).
writeU32
function writeU32(value: u32, buffer: ByteArray, bufferEnd: i32): inline (i32, i32)
Writes a u32 to a buffer. Buffer must have at least 10 bytes. Returns (startPos, endPos).
writeI64
function writeI64(value: i64, buffer: ByteArray, bufferEnd: i32): inline (i32, i32)
Writes an i64 to a buffer. Buffer must have at least 20 bytes. Returns (startPos, endPos).
writeU64
function writeU64(value: u64, buffer: ByteArray, bufferEnd: i32): inline (i32, i32)
Writes a u64 to a buffer. Buffer must have at least 20 bytes. Returns (startPos, endPos).
i32ToString
function i32ToString(value: i32): String
Converts an i32 to its decimal string representation.
u32ToString
function u32ToString(value: u32): String
Converts a u32 to its decimal string representation.
i64ToString
function i64ToString(value: i64): String
Converts an i64 to its decimal string representation.
u64ToString
function u64ToString(value: u64): String
Converts a u64 to its decimal string representation.
boolToString
function boolToString(value: boolean): String
Converts a bool to "true" or "false".
parseI32
function parseI32(s: String): inline (true, i32) | inline (false, _)
Parse a decimal integer from a string, with optional leading sign.
Returns inline (true, value) on success, inline (false, _) on failure.
Fails if the string is empty or contains non-digit characters (after optional sign).
decimalToF64
function decimalToF64(d: u64, p: i32, negative: boolean, inexact: boolean, tail: u64): f64
The f64 nearest to d * 10^p, correctly rounded. d is the decimal
significand as an integer below 2^64 and p its decimal exponent.
inexact says the true value lies strictly between d and d + 1
— the caller had more digits than it could keep — and tail is that
leftover fraction scaled by 2^11.
With 19 or fewer significant digits inexact is false and the
result is the correctly rounded one, always. Beyond that the
significand no longer fits: normalizing d to 64 bits leaves only
64 - bits(d) low bits for the remainder, so the value is placed to
within half of the last of them and can land on the wrong side of a
rounding boundary that falls inside that gap. Measured against a
correctly-rounded implementation this is about one input in five
thousand of more than 19 significant digits, always by one ulp
(dev/fp-difftest.js measures it).
Closing it needs an exact-arithmetic fallback, which the algorithm
this comes from also leaves to its caller.
f32ToString
function f32ToString(value: f32): String
The shortest decimal string that reads back as this f32. The f32
interval is used, not the f64 one the promoted value would have, so
0.1 as f32 prints "0.1" rather than "0.10000000149011612".
f64ToString
function f64ToString(value: f64): String
The shortest decimal string that reads back as this f64.
f64ToPrecision
function f64ToPrecision(value: f64, sigDigits: i32): String
value rounded to sigDigits significant decimal digits, in the
same format f64ToString uses. sigDigits is clamped to [1, 17].
parseF64
function parseF64(s: String): f64
Parses a decimal (or hexadecimal) number string to the NEAREST f64, correctly rounded.
The significand is accumulated as an integer and the exponent
tracked alongside, so no rounding happens until the single
decimalToF64 at the end. Digits beyond the nineteenth cannot
change the result except through the tie they might break, so they
are folded into a sticky low digit rather than dropped.
parseI64
function parseI64(s: String): inline (true, i64) | inline (false, _)
Parse an i64 integer from a string, supporting optional leading sign,
optional hex prefix (0x or 0X), and optional underscores (_).
Returns inline (true, value) on success, inline (false, _) on failure.
Variables
none
let none: None
The singleton None instance.