Records
Records provide a way to group related data together with named fields. They are declared using the is keyword and consist of field declarations (no branches).
Record Declaration
A record is declared similarly to a machine, but with a block containing only field declarations:
Request is {
method buf
path buf
body buf
}
Each field has a name and a type. Records are nominal — two records with identical fields are distinct types. Records are read-only in v1; changes require constructing a new record.
Construction
Records can be constructed in two ways: positionally or by named fields.
Positional Construction
Fields are provided in the order they are declared:
let r = Request("GET" "/" empty_buf)
The number and types of arguments must match the declared fields exactly.
Named Construction
Fields are provided by name, in any order:
let r = Request { method = "GET" path = "/" body = empty_buf }
Named construction is reordered to the declaration order internally. This form is useful when field order is not obvious or when constructing with clarity in mind.
Field Access
Access a field using the dot (.) operator:
r.method # : buf
r.path # : buf
r.body # : buf
Field access returns the type of the field.
Destructuring
Extract fields from a record using a let binding with a pattern:
let { method path body } = r
# now method, path, and body are available as local variables
Wildcards (_) can be used to ignore fields:
let { method _ _ } = r
# only method is bound; path and body are ignored
Patterns are positional (in declaration order). Nested patterns are supported for complex destructuring.
Example
+std.io.{ println print_buf }
Greeting is {
who buf
count i64
}
main is {
_ -> {
let g = Greeting("world" 3)
pin println(g.who)
let { who count } = g
pin print_buf(who)
pin println("!")
}
}
Generic Records
Records can take type parameters in square brackets, so the same shape works for many element types:
Box[T] is { val T }
Pair[K V] is { k K v V }
See the Generics chapter for construction and use-site annotations.
Error Codes
| Code | Description |
|---|---|
| E020 | Record constructor arity mismatch (wrong number of arguments) |
| E021 | Record constructor argument type mismatch |
| E022 | Named record construction missing required field |
| E023 | Named record construction contains unknown field |
| E024 | Named record construction has duplicate field |
| E025 | Field access to non-existent field |
| E026 | Destructure pattern arity mismatch |
| E027 | Destructure pattern type mismatch on nested field |
| E028 | Record name collides with machine, constant, or FFI declaration |
Limitations
Records have the following limitations:
- Read-only fields — no field mutation; reconstruct the whole record for changes. This is why the standard library’s collections (
Vec,String,IntMap) thread an updated value out of every mutating call. - No recursive types — a record cannot have a field of its own type.
- No field-rename destructure —
let { method = m } = ris not allowed; use positional patterns instead. - No type-tag matching — to match on which kind of value you have, use an enum rather than a record.