Pattern matching is a powerful tool that compares a value against a several patterns until it finds one that matches. These patterns can range from simple literal values to a complex assortment and combination of other patterns, most of which are covered below.
Perhaps most importantly, pattern Matches with are completeness
checked, so all possibilities for a given value must be covered.
For example, a match against an integer x with a single
0 (the equivalent of if x = 0) is rejected
as it does not have a case for when x != 0.
Every match expression must match a value against a list of patterns that are to be matched in order of declaration. There are several basic types of patterns: type cast patterns, operator patterns, destructures, clauses. Additionally, each type of pattern can be used within other patterns.
Type Cast Pattern
Type cast patterns are in the form
Type val and are matched
if val’s type is Type. This is how each possible value of a
tagged-union types is matched against. For example, the type
Maybe 't is one of two values:
Some 't. In pattern
matching syntax, this translates to:
//typeof t = Maybe i32 t = Some 2 match t with | None -> print "none :(" | Some val -> print val
A useful operator for patterns is the range operator
.. which can
be used to test if a value is in a given range.
match 103 with | -9..9 -> "1 digit" | -99..99 -> "2 digits" | _ -> "> 2 digits"
The append operator can also be used to check if a value has a given prefix or suffix:
match "my string" with | "my " ++ item -> "I have a " ++ item | "your " ++ item -> "You have a " ++ item | _ -> "Someone has something. Probably."
Adding a clause in a pattern causes the pattern to only be
matched when the clause is true. Clauses use the
keyword and are always boolean expressions. A clause also
has access to the bindings from the pattern it acts upon.
match 5 with | small given small < 3 -> "small!" | big given big > 10 -> "big!" | _ -> "moderate." match (3, "four") with | (x, "four") given x > 5 -> ... | (3, s) given len s > 3 -> ... | _ -> ...
Much of the time a clause can be replaced with an inline boolean expression.
match 5 with | _ < 3 -> "small!" | _ > 10 -> "big!" | _ -> "moderate." match (3, "four") with | (x > 5, "four") -> ... | (3, len s > 3) -> ... | _ -> ...
In match expressions any unbound identifier is bound to a value
based on its position in the pattern. For example,
s above refers
to the second index of the tuple type
(i32, Str), and thus is a
len is already bound to a function, so it is not
rebound. The explicit
given clause can help prevent the ambiguity
that arises if it is unclear if
s are already bound.
| can be placed between two patterns so that its
branch is matched if either of those patterns are true.
It is essentially the
or operator for patterns.
fib n = match n with | 0 | 1 -> 1 | _ -> fib (n-1) + fib (n-2)
Function parameters themselves are also patterns. This is best shown via example. The fib function above can be rewritten as follows:
fib (0|1) = 1 | n = fib (n-1) + fib (n-2)
A match expression shares much of the same semantics of an if expression in that the types of its branches must match and the value returned is the value of the matched branch.
assert (3 == match "hi" with | "yo" -> 1 | "hi" -> 3 | _ -> 5)