11 — Pattern Matching
Chapter goal: dispatch on shape and value with
match. Reference:../language-reference/statements.md§5. Example:../../examples/pattern_matching.aura.
match as a statement
def classify(value) -> str {
match value {
case 0 { return "zero" }
case 1 { return "one" }
case n if n > 100 { return "big" }
case n if n > 0 { return "positive" }
case n if n < 0 { return "negative" }
case _ { return "other" }
}
return "unreachable"
}
0 -> zero
1 -> one
42 -> positive
200 -> big
-7 -> negative
A case body is a block { ... } or an arrow -> expression. There is no fallthrough. : and => case syntax are rejected with a pointed message.
Patterns
| Pattern | Example | Binds |
|---|---|---|
| Wildcard | case _ | nothing |
| Literal | case 0, case "quit" | nothing |
| Binding | case n | n |
| Guard | case n if n > 0 | n, only if the guard holds |
| Or-pattern | case 1 | 2 | — |
| Enum member | case Color.RED | — |
| List/tuple destructuring | case [a, b], case [first, *rest] | a, b, … |
A guarded case does not count as a catch-all: a match over a scalar, bool or enum domain with no case _ (or bare binding) warns E109.
Matching strings
def command(name) -> str {
match name {
case "quit" { return "exiting" }
case "help" { return "showing help" }
case _ { return "unknown command" }
}
return ""
}
Destructuring
def sum_pair(pair) -> int {
match pair {
case [a, b] { return a + b }
case _ { return 0 }
}
return 0
}
*rest collects the remainder: case [first, *rest].
match as an expression
With arrow bodies, match produces a value:
def main() {
let n = 2
let text = match n {
case 1 -> "one"
case 2 -> "two"
case _ -> "other"
}
print(text) // two
}
The expression form is hoisted into a helper function; the tail expression of each case becomes the returned value.
Enums
enum Color { RED, GREEN, BLUE }
def main() {
let c = Color.RED
match c {
case Color.RED { print("red") }
case _ { print("other color") }
}
}
Members are comma-separated. Color.RED is a distinct value, never the string "Red".
Gotchas
breakin an arrow case swallows the nextcaseas a label. Because newlines are insignificant,case x -> breakfollowed bycase yparses asbreak case. Use a block body or a trailing;.matchneeds a catch-all for a finite domain, orE109warns (it never fails a build). A guardedcase _ if conddoes not suppress the warning.- There is no
whenkeyword; guards useif.
Choosing match over if
Use if/else if for a couple of boolean conditions. Use match when you are dispatching on shape (destructuring), on an enum, or on several literal values at once — it keeps the value being inspected in one place.
Patterns, precisely
A pattern is not an expression: it never computes, it only tests and binds. A bare lowercase name always binds (it does not compare against a variable of the same name); capitalized dotted members such as Color.RED compare against an enum member. That distinction is why case n is a catch-all binding while case Color.RED is a value test. To compare against a computed value, use a guard: case x if x == threshold.
An or-pattern case 1 | 2 matches when any alternative matches, and every alternative must bind the same names. Destructuring case [a, b] matches only a list/tuple of exactly two elements; add *rest to allow more.
A fuller example
enum Color { RED, GREEN, BLUE }
def describe(value) -> str {
match value {
case 0 { return "zero" }
case Color.RED { return "red" }
case [a, b] { return f"pair summing to {a + b}" }
case [first, *rest] { return f"list starting with {first}" }
case "hello" { return "the greeting" }
case _ { return "something else" }
}
return ""
}
zero
pair summing to 3
list starting with 1
the greeting
red
something else
Order matters: patterns are tried top to bottom and the first match wins. Put the most specific patterns first, and end with case _ when the domain is not exhaustive.
Gotcha: a bare binding pattern (case s) matches any value, so a guard on it such as case s if s.length() > 3 will also be reached by values that have no length — order the concrete cases first, or check the type in the guard.
Exercises
- Write
def sign(n) -> strusing literal and guard patterns. - Match on a list and return its head, or
nonewhen empty. - Build a two-value enum and dispatch on it, then remove
case _and observe theE109warning fromaura check.
What you learned
matchas a statement and as an expression.- Wildcard, literal, binding, guard, or, enum and destructuring patterns.
- No fallthrough;
->and{ }bodies; thebreakgotcha;E109.