Standard library
The standard library is organized into pure modules under the std:: prefix. All functions are standalone — there are no methods on values.
Signatures use generic type parameters (A, B, T, R) to indicate where types are determined by the call site. The iteration context type is abbreviated as:
IterCtx = Object {
index: number,
size: number,
isFirst: bool,
isLast: bool,
}
std::collections
Generic collection operations. Most work on both lists and objects via automatic lifting.
map
map(collection: [A], callback: |A| -> B) -> [B]
map(collection: [A], callback: |A, IterCtx| -> B) -> [B]
Apply a function to every element:
use std::collections::{ map }
doubled = map([1, 2, 3], |x| x * 2) // [2, 4, 6]
The callback may accept an iteration context as a second argument:
indexed = map([`a`, `b`, `c`], |item, ctx| {
return { value: item, index: ctx.index }
})
filter
filter(collection: [A], predicate: |A| -> bool) -> [A]
filter(collection: [A], predicate: |A, IterCtx| -> bool) -> [A]
Keep only elements for which the predicate returns true:
use std::collections::{ filter }
evens = filter([1, 2, 3, 4], |x| x % 2 == 0) // [2, 4]
flatMap
flatMap(collection: [A], callback: |A| -> [B]) -> [B]
flatMap(collection: [A], callback: |A, IterCtx| -> [B]) -> [B]
Map then flatten one level:
use std::collections::{ flatMap }
expanded = flatMap([1, 2, 3], |x| [x, x * 10]) // [1, 10, 2, 20, 3, 30]
find
find(collection: [A], predicate: |A| -> bool) -> A | null
find(collection: [A], predicate: |A, IterCtx| -> bool) -> A | null
Return the first matching element, or null if none matches:
use std::collections::{ find }
first_even = find([1, 3, 4, 5], |x| x % 2 == 0) // 4
missing = find([1, 3, 5], |x| x % 2 == 0) // null
any
any(collection: [A], predicate: |A| -> bool) -> bool
any(collection: [A], predicate: |A, IterCtx| -> bool) -> bool
Return true if at least one element satisfies the predicate:
use std::collections::{ any }
has_large = any([1, 2, 100], |x| x > 50) // true
all
all(collection: [A], predicate: |A| -> bool) -> bool
all(collection: [A], predicate: |A, IterCtx| -> bool) -> bool
Return true if every element satisfies the predicate:
use std::collections::{ all }
all_pos = all([1, 2, 3], |x| x > 0) // true
count
count(collection: [A], predicate: |A| -> bool) -> number
count(collection: [A], predicate: |A, IterCtx| -> bool) -> number
Count elements satisfying a predicate:
use std::collections::{ count }
n = count([1, 2, 3, 4], |x| x % 2 == 0) // 2
length
length(value: string) -> number
length(value: [A]) -> number
length(value: Object { ...: A }) -> number
Return the size of a string, list, or object:
use std::collections::{ length }
string_len = length(`rank`) // 4
list_len = length([1, 2, 3]) // 3
object_len = length({ http: 80, https: 443 }) // 2
reduce
reduce(collection: [A], initial: R, step: |R, A| -> R) -> R
reduce(collection: [A], initial: R, step: |R, A, IterCtx| -> R) -> R
Left fold with an explicit initial accumulator. Returns initial unchanged when the list is empty:
use std::collections::{ reduce }
sum = reduce([1, 2, 3, 4], 0, |acc, x| acc + x) // 10
reverse
reverse(collection: [A]) -> [A]
Return a reversed copy of a list:
use std::collections::{ reverse }
reversed = reverse([1, 2, 3]) // [3, 2, 1]
zip
zip(left: [A], right: [B]) -> [Object { left: A, right: B }]
Pair elements from two equal-length lists by index. Lists must have the same length:
use std::collections::{ zip }
pairs = zip([1, 2, 3], [`a`, `b`, `c`])
// [{ left: 1, right: `a` }, { left: 2, right: `b` }, { left: 3, right: `c` }]
groupBy
groupBy(collection: [A], keyFn: |A| -> string) -> Object { ...: [A] }
groupBy(collection: [A], keyFn: |A, IterCtx| -> string) -> Object { ...: [A] }
Group elements by a string key. Groups appear in first-encounter order; elements within each group preserve input order:
use std::collections::{ groupBy }
grouped = groupBy([1, 2, 3, 4], |x| x % 2 == 0 ? `even` : `odd`)
// { odd: [1, 3], even: [2, 4] }
keyBy
keyBy(collection: [A], keyFn: |A| -> string) -> Object { ...: A }
keyBy(collection: [A], keyFn: |A, IterCtx| -> string) -> Object { ...: A }
Index a list into an object by a key function. Duplicate keys are errors:
use std::collections::{ keyBy }
indexed = keyBy([{ id: `a`, v: 1 }, { id: `b`, v: 2 }], |item| item.id)
// { a: { id: `a`, v: 1 }, b: { id: `b`, v: 2 } }
sort
sort(list: [number]) -> [number]
sort(list: [string]) -> [string]
sort(obj: Object { ...: T }) -> Object { ...: T }
Sort a list of numbers or strings in ascending order. Sort an object's fields lexicographically by key. Stable:
use std::collections::{ sort }
sorted_list = sort([3, 1, 2]) // [1, 2, 3]
sorted_obj = sort({ zebra: 1, alpha: 2 }) // { alpha: 2, zebra: 1 }
sortBy
sortBy(collection: [A], keyFn: |A| -> number | string) -> [A]
sortBy(collection: [A], keyFn: |A, IterCtx| -> number | string) -> [A]
Sort a list by a projected numeric or string key. Stable ascending order:
use std::collections::{ sortBy }
people = [{ name: `carol`, age: 30 }, { name: `alice`, age: 25 }]
by_name = sortBy(people, |p| p.name) // alice first
std::list
List-specific operations.
range
range(end: number) -> [number]
range(start: number, end: number) -> [number]
range(start: number, end: number, step: number) -> [number]
Produce a list of consecutive integers over the half-open interval [start, end). Single-argument form treats start as 0:
use std::list::{ range }
r = range(5) // [0, 1, 2, 3, 4]
s = range(2, 5) // [2, 3, 4]
t = range(0, 10, 2) // [0, 2, 4, 6, 8]
flatten
flatten(list: [[A]]) -> [A]
Flatten one level of nesting:
use std::list::{ flatten }
flat = flatten([[1, 2], [3, 4]]) // [1, 2, 3, 4]
slice
slice(list: [A], start: number) -> [A]
slice(list: [A], start: number, end: number) -> [A]
Extract a sub-list over [start, end). Negative indices count from the end. Out-of-range indices are clipped:
use std::list::{ slice }
s = slice([10, 20, 30, 40, 50], 1, 3) // [20, 30]
t = slice([10, 20, 30, 40, 50], -2) // [40, 50]
transpose
transpose(list: [[A]]) -> [[A]]
Transpose a list of equal-length lists (swap rows and columns):
use std::list::{ transpose }
t = transpose([[1, 2], [3, 4], [5, 6]]) // [[1, 3, 5], [2, 4, 6]]
std::object
Object-specific operations.
mapValues
mapValues(obj: Object { ...: A }, callback: |A, string| -> B) -> Object { ...: B }
Transform each value. The callback receives (value, key). Preserves field order:
use std::object::{ mapValues }
doubled = mapValues({ a: 1, b: 2 }, |value, key| value * 2) // { a: 2, b: 4 }
keys
keys(obj: Object { ...: T }) -> [string]
use std::object::{ keys }
ks = keys({ a: 1, b: 2 }) // [`a`, `b`]
values
values(obj: Object { ...: T }) -> [T]
use std::object::{ values }
vs = values({ a: 1, b: 2 }) // [1, 2]
entries
entries(obj: Object { ...: T }) -> [Object { key: string, value: T }]
use std::object::{ entries }
es = entries({ a: 1, b: 2 })
// [{ key: `a`, value: 1 }, { key: `b`, value: 2 }]
fromEntries
fromEntries(entries: [Object { key: string, value: T }]) -> Object { ...: T }
Build an object from a list of { key, value } pairs. Duplicate keys are errors:
use std::object::{ fromEntries }
obj = fromEntries([{ key: `host`, value: `localhost` }, { key: `port`, value: 8080 }])
// { host: `localhost`, port: 8080 }
std::string
String-specific normalization and search helpers.
trim
trim(text: string) -> string
Remove leading and trailing Unicode whitespace:
use std::string::{ trim }
trimmed = trim(` rank `) // `rank`
split
split(text: string, delimiter: string) -> [string]
Split a string on a literal non-empty delimiter. Empty segments are preserved:
use std::string::{ split }
parts = split(`api,worker`, `,`) // [`api`, `worker`]
contains
contains(text: string, needle: string) -> bool
use std::string::{ contains }
has_inner = contains(`rank`, `an`) // true
startsWith
startsWith(text: string, prefix: string) -> bool
use std::string::{ startsWith }
has_prefix = startsWith(`rank`, `ra`) // true
endsWith
endsWith(text: string, suffix: string) -> bool
use std::string::{ endsWith }
has_suffix = endsWith(`rank`, `nk`) // true
isBlank
isBlank(text: string) -> bool
Return true when the string is empty after trimming whitespace:
use std::string::{ isBlank }
blank = isBlank(` `) // true
This is a convenient validation helper for field constraints and declaration constraints:
use std::string::{ isBlank }
@constraint(cond: |self| !isBlank(self))
service_name = `api`
std::Regex
Regular expression operations. Regex literals use the re prefix: refoo-[0-9]+``.
matches
matches(text: string, pattern: Regex) -> bool
matches(text: string, pattern: Regex, options: Object { caseInsensitive?: bool, multiline?: bool, dotAll?: bool }) -> bool
Return true when the pattern matches the entire string:
use std::Regex::{ matches }
valid = matches(`v1.2.3`, re`v[0-9]+\.[0-9]+\.[0-9]+`) // true
search
search(text: string, pattern: Regex) -> bool
search(text: string, pattern: Regex, options: Object { caseInsensitive?: bool, multiline?: bool, dotAll?: bool }) -> bool
Return true when the pattern matches anywhere in the string:
use std::Regex::{ search }
found = search(`deploy ABC-123 today`, re`[A-Z]+-[0-9]+`) // true
replace
replace(text: string, pattern: Regex, replacement: string) -> string
replace(text: string, pattern: Regex, replacement: string, options: Object { caseInsensitive?: bool, multiline?: bool, dotAll?: bool }) -> string
Replace all non-overlapping matches. Replacement strings are literal — no $1-style back-references:
use std::Regex::{ replace }
normalized = replace(`ticket ABC-123`, re`[A-Z]+-[0-9]+`, `TICKET`)
// `ticket TICKET`
captures
captures(text: string, pattern: Regex) -> Object { full: string, groups: [string | null] } | null
captures(text: string, pattern: Regex, options: Object { caseInsensitive?: bool, multiline?: bool, dotAll?: bool }) -> Object { full: string, groups: [string | null] } | null
Return the first match with its capture groups, or null if no match:
use std::Regex::{ captures }
result = captures(`ticket ABC-123`, re`([A-Z]+)-([0-9]+)`)
// { full: `ABC-123`, groups: [`ABC`, `123`] }
std::Time
Pure time operations over structural Time::Instant and Time::Duration values. No implicit access to the host clock.
Time::Instant = Object { epochMillis: number, offsetMinutes: number }
Time::Duration = Object { millis: number }
Time::TimeZone = `UTC` | `preserveOffset`
Time::parse
Time::parse(text: string, options: Object { format: `RFC3339` }) -> Time::Instant
use std::Time
instant = Time::parse(`2024-01-15T12:00:00Z`, { format: `RFC3339` })
Time::format
Time::format(instant: Time::Instant, options: Object { format: `RFC3339`, timezone: Time::TimeZone }) -> string
rendered = Time::format(instant, { format: `RFC3339`, timezone: `UTC` })
Time::add
Time::add(instant: Time::Instant, duration: Time::Duration) -> Time::Instant
later = Time::add(instant, Time::minutes(15))
Time::diff
Time::diff(left: Time::Instant, right: Time::Instant) -> Time::Duration
Returns the duration left - right.
Duration constructors
Time::seconds(n: number) -> Time::Duration
Time::minutes(n: number) -> Time::Duration
Time::hours(n: number) -> Time::Duration
std::Path
Pure relative filesystem-style path operations.
Path::RelativePath // opaque path value
Path::from
Path::from(text: string) -> Path::RelativePath
Parse a forward-slash-separated relative path string. Rejects absolute paths and upward escapes:
use std::Path
p = Path::from(`configs/production.yaml`)
Path::join
Path::join(segments: [string]) -> Path::RelativePath
Construct a path from a list of single segments (no / allowed within a segment):
deployment = Path::join([`k8s`, `deployment.yaml`])
Path::toString
Path::toString(path: Path::RelativePath) -> string
Return the canonical forward-slash rendering:
label = Path::toString(Path::join([`k8s`, `service.yaml`]))
// `k8s/service.yaml`
std::Emit
Entrypoint-only emit descriptor constructors. std::Emit<T> is not directly emit-compatible inside ordinary objects or lists; the host realizes it when it is returned from ordinary pub main, and serve-time JSON/YAML responses may also realize a top-level std::Emit<T> body before serialization.
Emit::Format
Emit::Format = `json` | `yaml` | `text`
Emit::manifest
Emit::manifest<T>: (Object {
entries: [Object {
path: Path::RelativePath,
format: Emit::Format,
value: unknown,
}]
}) -> std::Emit<T>
use std::Emit
use std::Path
manifest = Emit::manifest({
entries: [
{ path: Path::join([`k8s`, `deploy.yaml`]), format: `yaml`, value: { name: `api` } },
{ path: Path::from(`README.md`), format: `text`, value: `# api\n` },
]
})
pub main = || manifest
Duplicate destination paths are errors. text entries require a string value; json and yaml entries accept any ordinary emit-compatible Rank value. Emit::manifest(...) is pure: it does not write to disk until a host command such as rank <entry> --file-root <dir> chooses to materialize it.
std::Rand
Seeded deterministic randomness. No ambient entropy — all randomness is derived from an explicit seed value.
Rand::Rng = Object { algorithm: string, state: string }
Rand::Draw<T> = Object { value: T, rng: Rand::Rng }
Every draw function returns both the sampled value and the successor RNG, so sequencing is explicit.
Rand::seed
Rand::seed(seed: number) -> Rand::Rng
use std::Rand
rng = Rand::seed(42)
Rand::derive
Rand::derive(rng: Rand::Rng, label: string) -> Rand::Rng
Derive a stable labeled sub-stream. Use this to create independent sequences that do not interfere with each other:
color_rng = Rand::derive(rng, `color`)
size_rng = Rand::derive(rng, `size`)
Rand::int
Rand::int(rng: Rand::Rng, range: Object { min: number, max: number }) -> Rand::Draw<number>
Draw an integer in the inclusive range [min, max]:
{ value: roll, rng: rng2 } = Rand::int(rng, { min: 1, max: 6 })
Rand::float
Rand::float(rng: Rand::Rng) -> Rand::Draw<number>
Rand::float(rng: Rand::Rng, range: Object { min: number, max: number }) -> Rand::Draw<number>
Draw a float in [0, 1), or in [min, max) with explicit bounds.
Rand::bool
Rand::bool(rng: Rand::Rng, options?: Object { probability?: number }) -> Rand::Draw<bool>
Draw a boolean. probability defaults to 0.5.
Rand::choose
Rand::choose(rng: Rand::Rng, list: [A]) -> Rand::Draw<A>
Pick one element uniformly at random. Error if list is empty.
Rand::shuffle
Rand::shuffle(rng: Rand::Rng, list: [A]) -> Rand::Draw<[A]>
Return a randomly permuted copy of the list.
std::types
Compile-time utility types for deriving new types from existing ones. Import them from std::types and use them only in type positions.
use std::types::{ Pick, Omit, Partial, Required, Record, Exclude, Extract, NonNullable, DeepPartial }
User = Object {
id: string,
name: string,
email?: string,
passwordHash: string,
}
Summary = Pick<User, `id` | `name`>
PublicUser = Omit<User, `passwordHash`>
Draft = Partial<User>
CompleteDraft = Required<Partial<User>>
Flags = Record<`featureA` | `featureB`, bool>
TextOnly = Extract<string | number, string>
NumberOnly = Exclude<string | number, string>
Email = NonNullable<User[`email`]>
| Utility | Effect |
|---|---|
Pick<T, K extends keyof T> | Keep only fields K from object T |
Omit<T, K extends keyof T> | Remove fields K from object T |
Partial<T> | Make object fields optional |
Required<T> | Make object fields required |
Record<K, V> | Construct an object type with keys K and value type V |
Exclude<A, B> | Remove union members assignable to B from A |
Extract<A, B> | Keep union members assignable to B |
NonNullable<T> | Remove null from T |
DeepPartial<T> | Recursively make nested object fields optional |
Pick and Omit reject keys outside keyof T at the alias boundary.
keyof and indexed access types such as User[profile] are language operators rather than std::types imports; see Values and types.
std::Decode
Decode<T>(text) converts boundary text into a typed value of T. The result is T | null: null means the text did not match T.
use std::Decode
Port = 8080 | 8443 | 9000
decoded_port = Decode<Port>(`8443`) ?? 8080
SysEnv = Object {
PORT?: Decode<Port>,
DEBUG?: Decode<bool>,
...: string,
}
env = Env<SysEnv> {}
Use Decode<T> when external systems supply strings but the rest of the program wants numbers, booleans, or literal unions. It keeps parsing at the boundary instead of normalizing raw strings later in the program.
std::Mutation
std::Mutation is the standard-library surface for provider-backed writes and commit-aware output projection.
Surface
use std::Mutation
Mutation::Effect<T>
Mutation::Receipt<R>
Mutation::Succeeded<Value, Receipt>
Mutation::Rejected
Mutation::Unknown
Mutation::NotStarted
Mutation::Result<Value, Receipt>
Mutation::CommitReport<Ops>
Mutation::plan(...)
Mutation::commit(...)
There is no separate std::Mutate module or function. The surface name is std::Mutation.
At a high level:
Mutation::Effect<T>declares a provider-backed write whose fulfilled payload has typeTMutation::plan(...)collects labeled operations into one opaque plan valueMutation::commit(...)turns that plan into a top-levelstd::Emit<T>descriptorMutation::Result<Value, Receipt>andMutation::CommitReport<Ops>describe per-operation and aggregate commit outcomes
Use the std::Mutation names here as the raw surface reference. For the full lifecycle, examples, commit timing, result shapes, and constraints, see Mutations.
External input built-ins
These are part of the language, not importable modules.
Env<T> {}
Read environment variables from the host. The type parameter declares the expected schema. Only declared fields are materialized:
SysEnv = Object {
DATABASE_URL: string,
PORT?: string,
...: string,
}
env = Env<SysEnv> {}
port = env.PORT ?? `5432`
The environment is snapshotted exactly once per compilation.
File::Read<T> { path, format }
Read a structured file at a statically-known relative path:
Items = [Object { name: string, value: number }]
response = File::Read<Items> {
path: Path::from(`./data/items.json`),
format: `json`,
}
data = response.body
The result shape:
| Field | Type | Description |
|---|---|---|
body | T | Parsed, type-checked file contents |
ctx | Object { path: string, format: string } | Metadata about the read |
dotenv is also supported for .env-style flat text files:
- the decoded shape is a flat string-keyed object
- field-local
Decode<U>and schema-wideFile::Read<Decode<T>>use the same scalar-only decoding rules asEnv<T> {} - interpolation and shell evaluation are intentionally not supported
use std::Decode
Config = Object {
PORT?: Decode<8080 | 9000>,
DEBUG?: Decode<bool>,
...: string,
}
response = File::Read<Config> {
path: Path::from(`./.env`),
format: `dotenv`,
}
port = response.body.PORT ?? 8080
Supported formats: json, yaml, csv, dotenv. CSV rows decode to header-keyed objects with string values. Dotenv rows decode to flat text fields with optional Decode<...> at the same boundary. path must be statically evaluable before any external input execution.
HTTP::Fetch<T> { url, method, headers?, cacheKey }
Make a network request and decode the response body against T:
use std::HTTP
Body = Object {
users: [Object { id: number, name: string }],
}
response = HTTP::Fetch<Body> {
url: `https://api.example.com/users`,
method: `GET`,
headers: {
Accept: `application/json`,
},
cacheKey: `users-v1`,
}
users = response.body.users
status = response.ctx.status
Request fields:
| Field | Required | Description |
|---|---|---|
url | yes | Request URL |
method | yes | HTTP method (GET, POST, etc.) |
headers | no | Object of header name → value strings |
cacheKey | yes | Stable key for deterministic replay |
The result shape:
| Field | Type | Description |
|---|---|---|
body | T | Parsed, type-checked response body |
ctx | see below | Request metadata |
ctx fields:
| Field | Type | Description |
|---|---|---|
status | number | HTTP status code |
cacheKey | string | The cacheKey from the request |
headers | Object { ...: string } | Response headers |
cacheKey is required for determinism — see HTTP snapshot caching for how to capture and replay responses offline.