Pattern Matching
8.1 ? branching
aivi
classify = v => v ?
| 0 => "zero"
| _ => "nonzero"This is a concise way to do case analysis, similar to match in Rust or case in Haskell/Elixir.
Compiler checks:
- Non-exhaustive matches are a compile-time error unless a catch-all arm (
_) is present. - Unreachable arms (shadowed by earlier patterns) produce a warning.
8.2 Multi-clause functions
This is not a pipeline (|>). A leading | introduces an arm of a unary function that pattern-matches on its single (implicit) argument.
aivi
// Multi-clause unary function (matches on the implicit argument)
greet =
| None => "hi"
| Some name => "hi {name}"
// Equivalent explicit form
greet2 = u => u ?
| None => "hi"
| Some name => "hi {name}"aivi
sum =
| [] => 0
| [h, ...t] => h + sum t8.3 Record Patterns
aivi
greet =
| { role: Admin, name } => "Welcome back, Admin {name}!"
| { role: Guest } => "Welcome, guest!"
| { name } => "Hello, {name}!"8.4 Nested Patterns
Record patterns support dotted keys, so nested patterns can often be written without extra braces.
aivi
processResult =
| Ok { data.users: [first, ...] } => "First user: {first.name}"
| Ok { data.users: [] } => "No users found"
| Err { code: 404 } => "Not found"
| Err { code, message } => "Error {code}: {message}"Nested constructor patterns
Constructor patterns may themselves take pattern arguments, so you can nest them:
aivi
msg = res ?
| Err (NotFound m) => m
| Err _ => "no-msg"Flattened constructor-chain patterns
For readability, nested constructor patterns can be written without parentheses in pattern position:
aivi
// Parsed as: `Err (NotFound m)`
msg = res ?
| Err NotFound m => m
| _ => "no-msg"This "constructor chain" rule applies only in pattern context (after | and before =>).
8.5 Guards
Patterns can have guards using when:
aivi
classify =
| n when n < 0 => "negative"
| 0 => "zero"
| n when n < 10 => "small"
| n when n < 100 => "medium"
| _ => "large"8.6 Usage Examples
Option Handling
aivi
Option A = None | Some A
getOrDefault = default => v => v ?
| None => default
| Some value => value
userName = user.nickname |> getOrDefault "Anonymous"Result Processing
aivi
Result E A = Err E | Ok A
handleResult =
| Ok data => processData data
| Err e => logError e
// With chaining
fetchUser id
|> handleResult
|> renderViewList Processing
aivi
// Safe head
head =
| [] => None
| [x, ...] => Some x
// Take first n
take = (n, xs) => (n, xs) ?
| (n, _) when n <= 0 => []
| (_, []) => []
| (n, [x, ...xs]) => [x, ...take (n - 1, xs)]
// Zip two lists
zip =
| ([], _) => []
| (_, []) => []
| ([x, ...xs], [y, ...ys]) => [(x, y), ...zip (xs, ys)]Tree Traversal
aivi
Tree A = Leaf A | Node (Tree A) (Tree A)
depth =
| Leaf _ => 1
| Node left right => 1 + max (depth left) (depth right)
flatten =
| Leaf x => [x]
| Node left right => flatten left ++ flatten rightExpression Evaluation
aivi
Expr = Num Int | Add Expr Expr | Mul Expr Expr
eval =
| Num n => n
| Add a b => eval a + eval b
| Mul a b => eval a * eval b
// (2 + 3) * 4 = 20
expr = Mul (Add (Num 2) (Num 3)) (Num 4)
result = eval expr8.7 Expressive Pattern Orchestration
Pattern matching excels at simplifying complex conditional branches into readable declarations.
aivi
headerLabel = response ?
| { data.user.profile@{ name } } => name
| { data.guest: True } => "Guest"
| _ => "Unknown"Concise State Machines
aivi
// Update application state based on event
nextState = (state, event) => (state, event) ?
| (Idle, Start) => Running
| (Running, Pause) => Paused
| (Paused, Resume) => Running
| (Running, Stop) => Idle
| _ => state // Unchanged on invalid eventsExpressive Logic Branches
aivi
// Business rule mapping
discount = user => user ?
| _ when user.age > 65 && user.tier == Gold => 0.3
| _ when user.tier == Gold => 0.2
| _ when user.tier == Silver => 0.1
| _ => 0.0