Structs
A struct groups several named fields into one value. Use them when you have a few related pieces of data that belong together — coordinates, user records, configuration, anything where naming the fields makes the code clearer.
type Person = {
first_name: string
last_name: string
age: int
}
Construct an instance by calling the type name like a function. Arguments go in field order:
let frank = Person("Frank", "Smith", 34)
Read fields with .:
let fullname = frank.first_name .. " " .. frank.last_name
// "Frank Smith"
And update them the same way:
frank.age = frank.age + 1 // 35
Default field values
A field can have a default value. When constructing the struct, callers can leave any trailing field off:
type Greeter = {
name: string
greeting: string = "Hello"
excited: bool = false
}
extend Greeter {
fn greet(self) {
let punct = if self.excited { "!" } else { "." }
println(self.greeting .. ", " .. self.name .. punct)
}
}
Greeter("Alice").greet() // "Hello, Alice."
Greeter("Bob", "Howdy").greet() // "Howdy, Bob."
Named field arguments
You can also pass a field by name, which lets you skip past fields that have defaults:
Greeter("Carol", excited = true).greet() // "Hello, Carol!"
Greeter("Dave", greeting = "Hi", excited = true).greet() // "Hi, Dave!"
Named arguments must come after all positional arguments.
Generic structs
A struct can take a type parameter, so it works with any element type:
type Ref<T> = {
value: T
}
let int_ref = Ref(42) // Ref<int>
let str_ref = Ref("hello") // Ref<string>
See Generics for more.
Pattern matching
Structs can be destructured with patterns, in match and let. Every field must be matched, either all positionally (in declaration order) or all by name — see Patterns for details:
match p {
Point(0, 0) -> "origin"
Point(x = a, y = b) -> "(" .. a .. ", " .. b .. ")"
}
let Point(a, b) = p
let Point(x = a, y = b) = p
Adding behavior
To attach methods to a struct, use extend. To make it work with ==, sorting, printing, or other operators, implement an interface for it.