Records
A record type defines named, typed fields. Record objects have reference semantics. All operations on this page are Controls.
| Control | Result |
|---|---|
field(name, type) | Field descriptor |
record_type(name) | No value |
record(name, fields...) | No value |
record_of(type, values...) | New record |
get_field(record, names...) | Selected value |
set_field(record, names..., value) | No value |
field
Builds a field descriptor from a literal identifier and an evaluated type
description. The descriptor is consumed by record; it is not a runtime value.
A non-identifier name or a non-type second argument fails.
record
Defines a named record type. The name is a literal identifier. Field expressions are evaluated from left to right and must return field descriptors. Field order is the order supplied here, and every field name must be unique.
record(Person, field(name, string), field(age, integer))
declare(person, Person, record_of(Person, "Ada", 37))
print(get_field(person, name)) / Ada
A record can have zero fields: record(Empty) defines it and record_of(Empty)
constructs it. The type name is available to later type expressions.
Types are registered in the runtime, not confined to a block’s variable scope.
Defining an already defined record, conflicting with an existing non-record type, supplying duplicate field names, or supplying a non-field descriptor fails. A failed definition can leave its record name declared but undefined; there is no rollback of registration.
record_type
Declares an undefined record type so it can be referenced by other type
expressions before its fields are defined. record can later complete it.
This supports recursive and mutually referring record types.
record_type(Node)
record(Node, field(value, integer), field(next, optional(Node)))
declare(tail, Node, record_of(Node, 2, none))
declare(head, Node, record_of(Node, 1, tail))
print(get_field(head, next, value)) / 2
The name must be a literal identifier and must not already be declared.
An undefined record type cannot be used to create an object until record
defines its fields. Optional links allow recursive structures to terminate;
see optionals.
record_of
Evaluates a record type, then its value expressions from left to right. Creates an object with one value per field in declaration order. The count must match exactly, and each value must be accepted by the corresponding field type. A non-record type or an undefined record type fails.
record(Contact, field(name, string), field(phone, optional(string)))
declare(contact, Contact, record_of(Contact, "Ada", none))
print(to_string(contact)) / {"name":"Ada","phone":null}
Optional fields still need explicit arguments. Omission and none are not the
same thing.
get_field
Evaluates the record expression once, then follows one or more literal field identifiers in order. Field names are not evaluated as variables and must not be quoted. Every intermediate value must be a record.
record(Address, field(street, string))
record(Person, field(address, Address))
declare(person, Person, record_of(Person, record_of(Address, "Main Street")))
print(get_field(person, address, street)) / Main Street
A missing field, non-identifier field name, or non-record along the path fails.
An absent optional link is not traversed automatically. The final result can
be any value, including none or a reference to another object.
Use get_at for nested arrays and chain for mixed traversal:
record(Address, field(street, string))
record(Person, field(addresses, array(Address)))
declare(person, Person, record_of(Person, array_of(record_of(Address, "Main Street"))))
print(chain(link, person,
get_field(link, addresses),
get_at(link, 0),
get_field(link, street)
)) / Main Street
set_field
Follows literal field names with the same rules as get_field, then replaces
the final existing field. Evaluates the record once and validates the entire
path before evaluating the new value. The value must be accepted by that
field’s declared type. It does not create fields or intermediate records.
record(Address, field(street, string))
record(Person, field(address, Address))
declare(person, Person, record_of(Person, record_of(Address, "Main Street")))
set_field(person, address, street, "Oak Street")
print(get_field(person, address, street)) / Oak Street
An invalid path skips the value expression. A type mismatch is checked after that expression has run. Mutation affects the existing object and is visible through its other references. Produces no value; use a block ending in the object if a chain step also needs to return it.