Control flow
Match expressions
A match expression is used to match against one or more patterns and then evaluate the branch of the matched clause. All branches must evaluate to the same type. The clauses are tested sequentially, and the first clause that matches is evaluated.
match (3, "three hee hee")
case (1, "one fun fun") => "It's a 1"
case (_, "two doo loo") => "Got a 2"
case (3, description) => description // This one matches.
case _ => "There was nothing interesting :("
Guards can be added using if,
and the clause is only a match if both the pattern matches and the guard is true.
Guards can use bindings from the pattern.
match (3, 4)
case (x, y) if x > y => "x is bigger"
case (x, y) if x < y => "y is bigger"
case _ => "x and y are equal"
A match expression must always be exhaustive.
Guards interfere with the ability of the compiler to determine exhaustion.
For example, the last clause of the above example cannot have the guard x == y,
because the compiler cannot infer that the guards cover all cases.
If expressions
An if expression conditionally evaluates one of two branches and returns the result.
let x = 100;
if x > 20 then "big" else "small"
Patterns
A pattern simultaneously describes value and shape. Patterns can include:
- Names, such as
xorsecret_key, which always match. Int,Float, andStringvalue, which must match exactly.- Tuples, lists, and records.
- Value constructors.
The pattern _ matches anything, and it is idiomatic to mean that the value is not used.
Destructuring
Destructuring unpacks a composite value into its parts.
Tuples are destructured by position.
let (name, message) = ("Viviette", "Hello!")
Lists are destructured by position and the tail is captured with ..,
which can appear at most once in a list pattern and must be at the end of the list.
let [a, b, ..c] = [1, 2, 3, 4, 5]; // (a, b, c) == (1, 2, [3, 4, 5])
let [x, y, ..z] = [1, 2]; // (x, y, z) == (1, 2, [])
let [p, ..] = [1, 2, 3, 4, 5] // p == 1
Records are destructured by name,
and the remaining fields can be captured with ...
let { x, ... } = { x = 1, y = 2, z = 3 }; // x == 1
match { name = "Cheyenne", age = 44 }
case { name, age = 44 } => name // (name, age) = ("Cheyenne", 44)
case { .. } => "Person is not 44"
Value constructors are destructured by position.
type Groups a
case One a
case Two a a
case Three a a a
function first (group: Group a) -> a = match group
case One x => x
case Two x _ => x
case Three x _ _ => x
Irrefutability
A pattern is said to be irrefutable if it always matches.
For example, matching on (), x, or (a, b) will always succeed,
so these are irrefutable patterns.
If the pattern can fail (e.g., matching on an Option), then it is refutable.
Exhaustion
When matching with a match expression or function arguments, the pattern must be exhaustive,
meaning that every possible value of the match argument
must match with at least one of the patterns in the clauses.