Pattern Matching Deep Dive
Lateralus pattern matching is exhaustive and expressive. Match on structure, guards, bindings, and more. The compiler verifies you've handled all cases.
◉ Basic Match
let result = match value {
0 => "zero",
1 => "one",
_ => "many",
}
// Match is an expression - returns a value
let description = match status_code {
200 => "OK",
404 => "Not Found",
500 => "Server Error",
_ => "Unknown",
}
◉ Literal Patterns
// Numbers
match n {
0 => "zero",
1 | 2 | 3 => "small",
4..=10 => "medium",
_ => "large",
}
// Strings
match command {
"quit" | "exit" | "q" => exit(0),
"help" | "?" => show_help(),
"" => continue,
cmd => execute(cmd),
}
// Booleans
match active {
true => "Running",
false => "Stopped",
}
◉ Destructuring Structs
struct Point { x: float, y: float }
match point {
// Match specific field values
Point { x: 0.0, y } => println("on y-axis at " + str(y)),
Point { x, y: 0.0 } => println("on x-axis at " + str(x)),
// Bind all fields
Point { x, y } => println("at (" + str(x) + ", " + str(y) + ")"),
}
// Shorthand when variable name matches field
fn process(p: Point) {
let Point { x, y } = p // Destructure in let
println(x + y)
}
◉ Destructuring Enums
enum Result[T, E] {
Ok(T),
Err(E),
}
match result {
Ok(value) => println("Success: " + str(value)),
Err(e) => println("Error: " + e.message()),
}
// Nested destructuring
enum Message {
Quit,
Move { x: int, y: int },
Write(str),
Color(int, int, int),
}
match msg {
Quit => exit(0),
Move { x, y } => move_cursor(x, y),
Write(text) => println(text),
Color(r, g, b) => set_color(r, g, b),
}
◉ Guards
Add conditions with if:
match n {
x if x < 0 => "negative",
0 => "zero",
x if x > 100 => "large positive",
x if x % 2 == 0 => "even",
_ => "odd",
}
// Multiple conditions
match point {
Point { x, y } if x == y => "on diagonal",
Point { x, y } if x > 0 && y > 0 => "quadrant I",
Point { x, y } if x < 0 && y > 0 => "quadrant II",
Point { x, y } if x < 0 && y < 0 => "quadrant III",
Point { x, y } if x > 0 && y < 0 => "quadrant IV",
_ => "on axis",
}
◉ Destructuring Lists
match items {
[] => "empty",
[only] => "single: " + str(only),
[first, second] => "pair",
[first, ..rest] => "first: " + str(first) + ", rest: " + str(len(rest)),
}
// Head/tail pattern
fn sum(list: [int]) -> int {
match list {
[] => 0,
[head, ..tail] => head + sum(tail),
}
}
// Specific positions
match coords {
[x, y, z] => point_3d(x, y, z),
[x, y] => point_2d(x, y),
_ => panic("invalid coords"),
}
◉ Binding with @
Bind a value while also matching structure:
match msg {
// Bind whole variant while extracting fields
m @ Move { x, y } if x > 100 => {
println("Large move: " + str(x) + "," + str(y))
log(m)
},
_ => {}
}
// Bind range match
match age {
n @ 0..=17 => println("Minor, age " + str(n)),
n @ 18..=64 => println("Adult, age " + str(n)),
n @ 65.. => println("Senior, age " + str(n)),
}
◉ Option Matching
match find_user(id) {
Some(user) => greet(user),
None => println("User not found"),
}
// Nested option
match get_config("timeout") {
Some(Some(value)) => set_timeout(value),
Some(None) => set_timeout(default),
None => println("Config key missing"),
}
◉ Result Matching
match read_file(path) {
Ok(content) => process(content),
Err(IOError::NotFound) => create_file(path),
Err(IOError::Permission) => request_access(path),
Err(e) => panic("Unexpected: " + e.message()),
}
◉ Exhaustiveness
The compiler ensures all cases are handled:
enum Direction { North, South, East, West }
// ERROR: non-exhaustive pattern
match dir {
North => go_up(),
South => go_down(),
// Missing East and West!
}
// OK: all cases covered
match dir {
North => go_up(),
South => go_down(),
East => go_right(),
West => go_left(),
}
// OK: wildcard covers remaining
match dir {
North | South => vertical(),
_ => horizontal(),
}
◉ If Let
For single-pattern matching:
// Instead of:
match maybe_value {
Some(v) => process(v),
None => {},
}
// Use if let:
if let Some(v) = maybe_value {
process(v)
}
// With else:
if let Ok(data) = parse(input) {
use(data)
} else {
println("Parse failed")
}
◉ While Let
// Process until None
while let Some(item) = queue.pop() {
process(item)
}
// Process until error
while let Ok(line) = reader.read_line() {
println(line)
}
◉ Match in Pipelines
// Filter by pattern
items
|> filter(fn(x) {
match x { Some(_) => true, None => false }
})
// Transform by pattern
results
|> map(fn(r) {
match r {
Ok(v) => v * 2,
Err(_) => 0,
}
})
◉ Advanced: Nested Patterns
struct User { name: str, address: Option[Address] }
struct Address { city: str, country: str }
match user {
User { name, address: Some(Address { city: "NYC", .. }) } =>
println(name + " is in New York"),
User { name, address: Some(addr) } =>
println(name + " is in " + addr.city),
User { name, address: None } =>
println(name + " has no address"),
}
Pattern matching is central to Lateralus. Combined with exhaustiveness checking, it makes impossible states unrepresentable.