Pattern matching
Pattern matching is how Yona code inspects and decomposes values. It appears
in case expressions, function parameters, let bindings, and catch
clauses. A pattern either matches a value — binding any variables it
contains — or fails, in which case matching moves on to the next candidate.
Case expressions
Section titled “Case expressions”case value of pattern1 -> result1 pattern2 -> result2 _ -> fallbackendArms are tried strictly top to bottom; the first pattern that matches
(and whose guard, if any, passes) selects the arm, and its body becomes the
value of the whole expression. Later arms are not evaluated. If no arm
matches at runtime, the program aborts with a match error — so end with a
_ arm unless the patterns provably cover every case.
case n of 0 -> "zero" 1 -> "one" _ -> "many"endPattern forms
Section titled “Pattern forms”Literals
Section titled “Literals”Integers, floats, strings, characters, booleans, and symbols match by equality:
case status of :ok -> "success" :error -> "failure" :pending -> "waiting"endImplementation note. Symbols are interned to integers at compile time, so a
case over symbols compiles to an integer switch — dispatch is a single
comparison per arm, or a jump table.
Variables and wildcard
Section titled “Variables and wildcard”A lowercase name matches anything and binds it in the arm’s body. _
matches anything and binds nothing:
case point of (x, _) -> x # binds x, ignores the second componentendTuples
Section titled “Tuples”Tuple patterns match tuples of exactly that arity, position by position:
case (1, "hello", :ok) of (n, msg, :ok) -> msg # => "hello" (_, _, :error) -> "failed"endSequences — exact length
Section titled “Sequences — exact length”[], [x], [a, b] match sequences of exactly zero, one, two … elements:
case xs of [] -> "empty" [x] -> "one element" [a, b] -> "exactly two" _ -> "three or more"endSequences — head and tail
Section titled “Sequences — head and tail”[h|t] matches any non-empty sequence, binding the first element and the
remaining sequence. Multiple heads may precede the tail: [a, b | rest]
requires at least two elements. This is the primary way to recurse over
sequences:
sum xs = case xs of [] -> 0 [h|t] -> h + sum tend
sum [1, 2, 3, 4, 5] # => 15
case list of [x, y | rest] -> "starts with {x} then {y}" _ -> "fewer than two"endImplementation note. Taking head and tail of a persistent sequence is O(1), so head-tail recursion has no hidden copying cost — see Collections.
Constructors (ADTs)
Section titled “Constructors (ADTs)”Constructor patterns match a specific variant of an
algebraic data type and bind its fields positionally.
Patterns nest arbitrarily. Prelude constructors such as Some/None work
in an expression program; your own type declarations belong in a
module (see Modules):
let maybeValue = Some 42 incase maybeValue of Some x -> x * 2 None -> 0endmodule Demo\Tree
export depth
type Tree a = Node (Tree a) a (Tree a) | Leaf
depth t = case t of Leaf -> 0 Node l _ r -> 1 + (if depth l > depth r then depth l else depth r)endNamed fields (records)
Section titled “Named fields (records)”ADTs with named fields match with Constructor { field = pattern, … }.
You only name the fields you care about:
module Demo\People
export greet, ageOf
type Person = Person { name : String, age : Int }
greet person = case person of Person { name = n, age = a } -> "{n} is {a}"end
ageOf person = case person of Person { age = a } -> a # other fields ignoredendOr-patterns
Section titled “Or-patterns”| between patterns matches if any alternative matches. Alternatives share
one arm body:
case x of 1 | 2 | 3 -> "small" _ -> "big"endGuards
Section titled “Guards”A pattern may carry an if guard; the arm is taken only when the pattern
matches and the guard (which may use the pattern’s bindings) is true.
A failed guard falls through to the next arm:
case x of 0 -> "zero" n if n > 0 -> "positive" _ -> "negative"endTyped patterns
Section titled “Typed patterns”(name : Type) matches on the runtime type of a value from an anonymous
sum type like Int | String, binding it at the annotated type:
describe : Int | String -> Stringdescribe v = case v of (n : Int) -> "number {n}" (s : String) -> "text {s}"end
describe 42 # => "number 42"describe "hello" # => "text hello"As-bindings Partial
Section titled “As-bindings Partial”name@pattern matches the pattern and additionally binds the whole value
to name. Parser and type-checker support is in place, but code generation
for as-bindings in case arms is still limited — prefer rebinding
explicitly when it fails to compile.
case xs of all@[h|_] -> (h, all) # first element and the whole sequence [] -> (0, [])endDictionary patterns Partial
Section titled “Dictionary patterns Partial”The grammar reserves { :key: pattern, … } for matching dictionary
entries by key, but compiler support is currently limited. Use
Std\Dict::get/contains to inspect dictionaries instead — see
Collections.
Patterns outside case
Section titled “Patterns outside case”In let bindings
Section titled “In let bindings”A let binding’s left-hand side may be a pattern; it destructures the
value. The pattern must match — a failed let pattern is a runtime error.
let (a, b) = (1, 2), [h|t] = [10, 20, 30] ina + b + h # => 13In function parameters
Section titled “In function parameters”Every function parameter is a pattern, and multiple clauses give per-constructor definitions (see Functions):
first pair = case pair of (a, _) -> aend
first (1, 2) # => 1
unwrap x = case x of Some v -> v None -> 0endIn catch clauses
Section titled “In catch clauses”Exceptions are ADT values, and catch clauses are patterns over them.
Unmatched exceptions propagate to the next handler up the stack:
type Error = RuntimeError String | NotFound String
try riskyOperationcatch RuntimeError msg -> "runtime: " ++ msg NotFound path -> "missing: " ++ path _ -> "unknown failure"endExhaustiveness
Section titled “Exhaustiveness”When the scrutinee is an ADT, the compiler checks that the arms cover every constructor and emits a warning (not an error) for each missing one:
type Color = Red | Green | Blue
case color of Red -> "red" Green -> "green"end# Warning: non-exhaustive pattern match on Color — missing constructor BlueA _ or variable arm makes any match exhaustive. Heed these warnings: a
non-exhaustive match that falls off the end aborts at runtime.
Where to next
Section titled “Where to next”- Types and data — defining the ADTs you match on.
- Collections — sequence, dict, and set operations.
- Language specification — the full pattern grammar.