Skip to main content

Patterns

A pattern simultaneously describes value and shape. Patterns can include, for example, x, True, [1, 2, 3], and ("hello", x). These patterns can be broken down:

  • The pattern x matches anything.
  • The pattern True matches the shape Bool with the value True.
  • The pattern [1, 2, 3] matches the shape List Int of [_, _, _] with respective values 1, 2, and 3.
  • The pattern ("hello", x) matches the shape (String, _) where the first value is "hello" and the second is any value.

Patterns can be used in several locations.

  1. Let statements (must be irrefutable).
    let x = 5
    let (message, number) = ("hello", 22)
  2. Match expressions.
    let value = match Some(1001) {
    Some(x) => x
    None => 0
    }
  3. Function arguments (must be irrefutable).
    def sum_pairs((a, b): (Int, Int), (x, y): (Int, Int)) -> (Int, Int) {
    (a + x, b + y)
    }
    assert_eq(sum_pairs((10, 20), (3, 4)), (13, 24))

A pattern in a let binding must be irrefutable, meaning that it always matches. If the pattern can fail (e.g., matching on an Option), it cannot be used in a let binding.

When a match expression is used, it must be exhaustive over the list of potential matches, meaning that every possible value of the match argument must match with at least one of the patterns and guards.

Values in patterns

Patterns can include value literals.

match (3, 1) {
(3, _) => "first is 3"
(2, 2) => "both are 2"
_ => "something else"
}

Destructuring patterns

Destructuring unpacks a composite value into its parts. This applies to tuple-like types, record-like types, and list types. Tuple-like values are simply destructured by position.

let (name, message) = ("Viviette", "Hello!")

Record-like types can use .. to omit fields and : to rename others. Field labels can appear in any order; however, the .. must appear last.

type Person(name: String, age: Int)
let Person(age: years, ..) = Person(name: "Viviette", age: 27)
assert_eq(years, 27)
// Uncommenting the following line results in a compile-time
// error as age is not defined, only years.
// assert_eq(age, "Viviette")

Lists can be destructured using .., which can appear at most once in a list pattern.

// Capture the tail.
let [a, b, ..c] = [1, 2, 3, 4, 5]
assert_eq((a, b, c), (1, 2, [3, 4, 5]))

// Capture at an arbitrary position in the list.
let [x, ..y, z] = [1, 2, 3, 4, 5]
assert_eq((x, y, z), (1, [2, 3, 4], 5))

// It can also be empty.
let [p, ..q, r] = [1, 2]
assert_eq((p, q, r), (1, [], 2))

// The name can also be omitted.
let [u, .., v] = [1, 2, 3, 4, 5]
assert_eq((u, v), (1, 5))

Outer match patterns

An outer match uses @ to match an entire value and its structure.

type Person(name: String, age: Int)
let person @ Person(name, ..) = Person(name: "Viviette", age: 27)
// Both the `person` and `name` are bound to values.
assert_eq(person, Person(name: "Viviette", age: 27))
assert_eq(name, "Viviette")