Pattern Matching
Pattern matching pulls apart compound data by writing a template of it, with variables as holes to be filled. It's pervasive in Raven: assignments, function signatures and match clauses are all patterns.
Destructuring assignment
The left side of = can be a list or constructor pattern:
[a, b] = ["a", "b"]
# a == "a", b == "b"
m = Some(5)
Some(x) = m
show x # x = 5
Use _ to ignore a position:
[_, a, _] = [1, 2, 3] # a = 2
Patterns nest arbitrarily: Some([x, Complex(re, _)]) = m is fine.
A match that fails is an error – Some(x) = Nil() aborts. For matches that might fail, you want if let or match.
Matching values
A literal in a pattern matches that exact value. To match against the value held in a variable (rather than binding a fresh one), prefix it with $:
fn answer($Answer) { ... }
This matches the value stored in Answer, rather than binding a new argument named Answer.
Matching types
x: T matches any value of type T, binding it to x. Types can be combined into unions with |, and some types take parameters in square brackets:
fn Char(x: (Int | UInt)) { ... }
bundle Char(val: UInt[21]) # a 21-bit unsigned integer
if let and match
Handling both the success and failure of a match is the job of if let:
if let Some(x) = m {
println("x is not nil :D")
} else {
println("x is nil :(")
}
When there are several cases, use match:
match m {
let Some(x) { println("x is not nil :D") }
let Nil() { println("x is nil :(") }
}
match tries each let clause in turn, running the first body whose pattern matches; if none match, it's an error. match is generally the best option, except where if let would be significantly terser. (In future, match will check that you've covered all cases.)
Signatures are patterns
Function signatures are the same pattern language. fn foo(a, b) is really a match over the argument list, and a definition like
fn nil?(Some(x)) { false }
fn nil?(Nil()) { true }
is a third way of writing the branch above – the function dispatches to whichever definition matches. This is covered properly in Multiple Dispatch.
Extensible matching: traits
Type patterns aren't limited to concrete bundles. Traits like Number, Real and Integer match whole families of types, and they're ordinary library code – a trait is defined by adding methods to the matchTrait function:
Even = tag"/Even"
@extend
fn matchTrait($Even, x: Int64) {
if rem(x, 2) == 0 { Some(x) }
}
Now fn half(x: Even) matches only even integers. The standard library's own numeric tower (Number, Real, Integer) is built exactly this way, and Any and NotNil are traits too. You can also test a pattern explicitly with isa?(x, T).