Skip to content

Matching

Gale matches values by shape. Each arm pairs a pattern with a result. The first pattern that fits wins. The compiler rejects a case that leaves a shape uncovered.

A name in a pattern binds a new value. Pin it with ^ to compare against a value already in scope. Use separate arms for separate patterns. "GET " <> rest binds the rest of a string. value when value in [:debug, :info] narrows an atom in a guard.

mod matching
pub fn describe(status: :idle | :running | :done) -> String {
case status {
:idle -> "waiting"
:running -> "busy"
:done -> "finished"
}
}

case walks its arms top to bottom and picks the first pattern that fits the value. Every shape of the matched type must be covered. The compiler checks this before the program runs, so a missing arm is an error, not a runtime surprise:

pub fn broken(status: :idle | :running | :done) -> String {
case status {
:idle -> "waiting"
:running -> "busy"
}
}

A literal arm matches one value. A guard (when) attaches a Boolean test to an arm: the arm is chosen only when both the pattern fits and the test holds. _ fits anything, so it closes out the remaining cases.

mod matching
pub fn stock(count: Integer) -> String {
case count {
0 -> "empty"
n when n < 10 -> "low"
_ -> "many"
}
}

A name written in a pattern is a new binding for whatever value sits at that position. It never compares against anything you already hold. _ ignores a position. Used together they destructure data into fresh names:

mod matching
pub type Entry = {Integer, String}
pub fn name_of(entry: Entry) -> String {
case entry {
{_, name} -> name
}
}
pub fn first(items: List<Integer>, fallback: Integer) -> Integer {
case items {
[] -> fallback
[head | _] -> head
}
}

So {wanted, _} would match every entry: it simply names the first element wanted. To compare a position against a value you already hold, pin the name with ^. A pin binds nothing; the arm fits only when the position equals the pinned value at the moment the case starts:

mod matching
pub type Entry = {Integer, String}
pub fn has_id(entry: Entry, wanted: Integer) -> Boolean {
case entry {
{^wanted, _} -> true
_ -> false
}
}

Because a pinned arm can fail on a value its shape admits, it never counts toward coverage. The _ fallback above is required. The same ^name works anywhere a pattern does, including receive, where pinning a reply tag lets a process skip messages it is not waiting for (see Processes).

Gale follows Elixir’s pattern syntax: | in a pattern is the list-tail marker, not an alternative operator. Write one arm for each alternative. This keeps source control flow identical to the native clauses the BEAM executes.

mod matching
pub fn polarity(sign: :neg | :zero | :pos) -> String {
case sign {
:neg -> "signed"
:pos -> "signed"
:zero -> "zero"
}
}

A prefix string pattern matches a non-empty literal and binds the rest. On a String scrutinee, rest is String.

mod matching
pub fn verb(line: String) -> String {
case line {
"GET " <> rest -> rest
_ -> line
}
}

in [atoms] is a guard. When every literal is an atom, the bound value is refined to that atom union for the arm.

mod matching
pub fn shown?(level: Atom) -> Boolean {
case level {
value when value in [:debug, :info, :warning] -> true
_ -> false
}
}

Use cond to choose the first true condition. Each condition must be Boolean, and the final condition must be the literal true:

pub fn sign(value: Integer) -> Integer {
cond {
value > 0 -> 1
value < 0 -> -1
true -> 0
}
}