Match

match runs the branch whose pattern fits a value. Each branch starts with when, lists one or more patterns separated by commas, and ends at the next when, else, or end:

func area(Shape shape) f64 do
    match shape do
        when CIRCLE(radius) then
            return 3.14159 * radius * radius
        when RECT(width, height) then
            return width * height
        when EMPTY then
            return 0.0
    end
end

On an enum, patterns are member names. They may be written plainly (CIRCLE) or with their enum (Shape.CIRCLE). A member pattern can give a pattern for each value the member carries, in order, or for some of them by the names the member gives them (RECT(height: h)). A name binds the value, which is then a variable in that branch; _ skips a value, and leaving out the parentheses skips them all. Any other pattern fits only some values, as in ERROR(EMPTY) or SOME(0):

func message(Result[int, ParseError] result) str do
    match result do
        when OK(0) then
            return "zero"
        when OK(n) then
            return f"the number {n}"
        when ERROR(EMPTY) then
            return "nothing to read"
        when ERROR(BAD_DIGIT(c)) then
            return f"'{c}' is not a digit"
    end
end

A when with several patterns cannot bind names. A match on an enum must handle every member, unless it has an else; a branch handles a member when its patterns for the member's values fit every value and it has no guard. The compiler lists any member that is missing. A function whose last statement is such a match, with a return at the end of every branch, always returns.

Integers, characters, strings, and booleans match by value. Patterns are literals, negative numbers, constants, and, for integers and characters, ranges that hold both of their ends:

match command do
    when "start", "go" then
        print("starting")
    when "stop" then
        print("stopping")
    else
        print("unknown command")
end

match c do
    when 'a' to 'z', 'A' to 'Z' then
        print("letter")
    when '0' to '9' then
        print("digit")
end

A match on these types runs no branch when nothing fits and there is no else. A value may appear in only one pattern, and no value in two ranges written with literals. Floats cannot be matched, since comparing floats for equality is rarely meaningful; bind a name to the value and compare it in a guard.

A name at the top of a match on any of these types, on a struct, or on a tuple binds the value, unless it is a constant: it fits every value, as _ does, and makes the match handle every value, as an else does. The name of a variable is an error there, since a new variable would hide it; compare with it in a guard, as in when n if n == limit.

A struct is matched with its name and patterns for its fields, in order or some of them by name. null fits a struct that is null, which no other struct pattern does:

func place(Point p) str do
    match p do
        when null then
            return "nowhere"
        when Point(0, 0) then
            return "origin"
        when Point(x: 0) then
            return "on the y axis"
        when Point(x, y) then
            return f"at {x}, {y}"
    end
end

Such a match handles every value when it has null and a struct pattern whose patterns fit every value, or a name.

A tuple is matched with a pattern for each item, in parentheses. An item's pattern can be any pattern, a name binding the item's value:

func describe((int, int) point) str do
    match point do
        when (0, 0) then
            return "origin"
        when (0, y) then
            return f"on the y axis at {y}"
        when (x, y) then
            return f"at {x}, {y}"
    end
end

match (command, count) do
    when ("add", 1) then
        print("one more")
    when (Option.SOME(n), _) then
        print(n)
end

Inside another pattern, a name is a constant or an enum member when one has it, and otherwise a new variable. Branches are tried in order; a branch whose pattern fits every value, such as (x, y), makes the match handle every value, as an else does. Several patterns in one when cannot bind names. To match several values at once, match a tuple of them, as in match (command, count) do.

Guards

if after the patterns of a when adds a guard: the branch runs only when a pattern fits and the guard is true, and otherwise the branches after it are tried. The guard sees the names the patterns bind:

match score do
    when n if n > 100 then
        print("too high")
    when 90 to 100 then
        print("A")
    when n then
        print(f"{n} points")
end

A branch with a guard handles no values for the rest of the match: a member it names is still missing, and the values it names may appear again.

break and continue inside a match apply to the loop around it.