type User {
id: Int
name: Str
}
proc main(): void {
u := User(1, "ada")
echo u.name
echo User(name: "grace", id: 2).id // by name, in any order
}type Event {
at: Int // outside every variant: every one has it
Opened { by: Str }
Closed
}
proc main(): void {
e := Event.Opened("ada", 5) // the variant's own field first, then `at`
echo e.at // 5 — a shared field is read straight off
// echo e.by // compile error: `by` belongs to Opened
for each ev in [e, Event.Closed(9)] {
echo ev.at
}
}type Expr {
Num { value: Int }
Add { left: Expr, right: Expr }
}
type Folder {
name: Str
children: Folder[dyn] // through a vector
}
proc main(): void {
sum := Expr.Add(Expr.Num(1), Expr.Num(2))
echo sum
echo Folder("root", [Folder("docs", [])]).name
}A type with no variants is a struct; with variants, a tagged union. A value is built by calling the type or the variant — User(1, "ada"), Shape.Circle(5) — and a variant that carries nothing is still called: Shape.Point(), since Shape.Point on its own names the variant rather than a value of it. Fields may be passed by name, in any order. There is no subtyping and no interface: a variant is a way of building and matching a union, not a type of its own.
A field declared outside every variant is added to all of them, after each variant's own fields, and it is the one kind of field a union value has: event.at reads it straight off. Every other field belongs to its variant and is reached by matching with is, on the next slides — even one that every variant happens to declare under the same name. A union value is built whole, so no field of one is assigned on its own.
A type may hold itself through a variant, which is what a tree needs, or through a vector. A struct that holds itself directly, or through a Result, could never be finished, and is refused.