Skip to content

Sum Types and Pattern Matching

Sum types are declared with the type keyword. Each variant either carries named fields or is a plain unit variant:

type Direction {
North,
South,
East,
West,
}
type Shape {
Circle(radius: Double),
Rectangle(width: Double, height: Double),
}
let dir = North // unit variant. No parentheses
let c = Circle(radius: 5.0) // named fields
let c2 = Circle(5.0) // positional. Same as above

Unit variants are referenced by name alone. North() is a compile error.

match is Vertex’s primary tool for working with sum types. The compiler verifies that every variant is covered:

fn area(s: Shape): Double {
match s {
Circle(radius: r) => 3.14159 * r * r,
Rectangle(width: w, height: h) => w * h,
}
}

When the binding name matches the field name, use field: instead of field: field:

// Long form
match s { Circle(radius: radius) => radius * 2.0 }
// Shorthand. Equivalent
match s { Circle(radius:) => radius * 2.0 }
match score {
n if n >= 90 => "A",
n if n >= 80 => "B",
_ => "F", // wildcard: matches anything
}
let msg = match isAdmin, isActive {
true, true => "Active admin",
true, false => "Inactive admin",
false, _ => "User",
}
match status {
"active" | "pending" => true,
_ => false,
}

When the compiler knows the exact variant statically, you can access fields directly:

let c = Circle(radius: 5.0)
debug c.radius // 5.0. No match needed; static type is Circle

When every variant shares a field with the same name and type, you can access it on the parent type too:

type Ticket {
VIP(price: Int, available: Int),
Standard(price: Int, available: Int),
}
fn getAvailable(t: Ticket): Int {
t.available // valid. Both variants have 'available: Int'
}
let assert Ok(value) = safe_divide(10, 2)

Prefer if let for conditional extraction or match for exhaustive dispatch.