Skip to content

Records, structs, and maps

A record is a labeled product such as { name: Binary, age: Integer }. It needs no declaration, emits a BEAM map with atom keys, and supports field access and immutable update. Width subtyping lets a value carry more fields than the receiving function requires.

Map<K, V> is a separate typed dictionary for keys that are not known statically; construct one with gale_std.map.

module structs
pub fn birthday(user: { name: Binary, age: Integer }) -> { name: Binary, age: Integer } {
user { age: user.age + 1 }
}
pub fn origin() -> { x: Integer, y: Integer } {
{ x: 0, y: 0 }
}

A struct is nominal and occupies its own file. The struct block follows the module header; ordinary functions follow the block. A struct is assignable to a compatible record, but a plain record is not assignable to the struct.

module structs.user
@moduledoc """
A struct is nominal and occupies its own file. The `struct` block follows the
module header; ordinary functions follow the block. A struct is assignable to
a compatible record, but a plain record is not assignable to the struct.
"""
struct {
name: Binary,
age: Integer
}
pub fn ada() -> User {
User { name: "Ada", age: 36 }
}
pub fn birthday(user: User) -> User {
user { age: user.age + 1 }
}

Map<K, V> is different from both: it is a dictionary whose keys are not known statically. Construct maps with gale_std.map; {} is rejected; it is not an empty map. Map keys are invariant, while values are covariant.

module structs
pub fn scores() -> Map<Binary, Integer> {
gale_std.map.from_list([("ada", 36), ("al", 41)])
}
pub fn score(scores: Map<Binary, Integer>, name: Binary) -> Option<Integer> {
gale_std.map.get(scores, name)
}

gale_std.map.get returns Option<V>, preserving the distinction between a missing key (None) and a stored nil (Some(nil)).