Pattern Matching
match selects one of several branches by structurally inspecting a value. Unlike a C-style switch, every arm is independent (no fall-through), patterns can bind parts of the value, and the typechecker requires the arms to be exhaustive — so a forgotten enum variant or unhandled nil is a compile error, not a production incident.
match is also an expression: every arm produces a value of the same type, and the whole match evaluates to that value.
Basic Match
Section titled “Basic Match”enum Status Ok Warn Errorend
local status: Status = Status.Warn
local label: string = match status case Status.Ok then "fine" case Status.Warn then "watch out" case Status.Error then "oops"end
println(label) -- watch outEach arm is case <pattern> then <expression-or-block>. The block runs until the next case or the closing end.
Matching Enum Payloads
Section titled “Matching Enum Payloads”When a variant carries data, the pattern binds those fields into locals visible inside the arm:
enum Event Click(x: integer, y: integer), Key(code: string), Quitend
fn describe(e: Event) -> string return match e case Event.Click(x, y) then "click at " .. x .. "," .. y case Event.Key(code) then "key: " .. code case Event.Quit then "bye" endendMatching Nullables
Section titled “Matching Nullables”match is the cleanest way to consume a T? — nil is just another pattern:
class User name: string = "" fn init(name: string) self.name = name endend
class Repo fn findById(id: integer) -> User? if id == 1 then return User("Ada") end return nil endend
local repo: Repo = Repo()local id: integer = 1
match repo.findById(id) case nil then println("not found") case user then println("hi " .. user.name) -- hi AdaendThe bare name user here is a binding pattern: it captures the non-nil value. Inside that arm user has type Player, not Player?.
Wildcard and Bindings
Section titled “Wildcard and Bindings”Use _ to ignore a value and any identifier to bind it:
local n: integer = 7
match n case 0 then println("zero") case 1 then println("one") case other then println("something else: " .. other) -- something else: 7end
fn pair() -> (integer, integer) return 0, 5end
match pair() case (0, _) then println("x is zero") -- x is zero case (_, 0) then println("y is zero") case _ then println("neither")endA pattern that starts with a lowercase identifier (and isn’t an enum variant) binds; _ matches anything without binding.
Guards with when
Section titled “Guards with when”Add a condition that must also hold for the arm to fire:
local n: integer = 5
match n case x when x < 0 then println("negative") case 0 then println("zero") case x then println("positive " .. x) -- positive 5endGuards are evaluated after the pattern matches. If the guard is false, matching continues with the next arm.
Literal Patterns
Section titled “Literal Patterns”Numbers, strings, booleans, and nil are all valid patterns:
fn showHelp() println("help")end
local command: string = "help"
match command case "quit" then println("bye") case "help" then showHelp() -- help case other then println("unknown: " .. other)endMultiple Return Values
Section titled “Multiple Return Values”Match destructures tuples returned by multi-value functions:
fn divmod(a: integer, b: integer) -> (integer, integer) return a / b, a % bend
local a: integer = 17local b: integer = 5
match divmod(a, b) case (q, 0) then println("clean: " .. q) case (q, r) then println(q .. " rem " .. r) -- 3 rem 2endExhaustiveness
Section titled “Exhaustiveness”The typechecker verifies that every possible value of the scrutinee is covered:
enum Color Red, Green, Blueend
local c: Color = Color.Red
local name: string = match c -- ERROR: non-exhaustive, missing `Color.Blue` case Color.Red then "red" case Color.Green then "green"endAdd the missing variant or a wildcard arm to fix it. For nullables, both nil and a binding (or _) must be covered. Guards never count toward exhaustiveness — if every arm has a guard, you still need a wildcard fallback.
As an Expression
Section titled “As an Expression”Because match produces a value, it composes naturally with local, return, and arguments:
enum Tier Free Pro Enterpriseend
fn priceFor(tier: Tier) -> integer return match tier case Tier.Free then 0 case Tier.Pro then 9 case Tier.Enterprise then 99 endend
println(priceFor(Tier.Pro)) -- 9If used as a statement, the result is simply discarded — same rule as any other expression.