Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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

CodeDescription
E020Record constructor arity mismatch (wrong number of arguments)
E021Record constructor argument type mismatch
E022Named record construction missing required field
E023Named record construction contains unknown field
E024Named record construction has duplicate field
E025Field access to non-existent field
E026Destructure pattern arity mismatch
E027Destructure pattern type mismatch on nested field
E028Record 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 destructurelet { method = m } = r is 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.