Match expressions and pattern matching

Amatch expressionhas the general form:

match e with
  pattern_1 -> block_1
  pattern_2 -> block_2pattern_n -> block_n

Whereeis an expression, called thescrutineeof the match expression, and eachcasehas apattern to match against the value of the scrutineeand ablockto evaluate in case it matches.

The evaluation semantics of match expressions are as follows:

  1. The scrutinee is evaluated.
  2. The first pattern is evaluated and matched against the value of the scrutinee.
  3. If the pattern matches, any variables in the pattern are substituted into the block to the right of its->(called thematch body)and the block is evaluated. If the pattern doesn’t match then the next pattern is tried and so on.

It's possible for Unison to actually evaluate cases in a different order, but such evaluation should always have the same observable behavior as trying the patterns in sequence.

It is an error if none of the patterns match. In this version of Unison, the error occurs at runtime. In a future version, this should be a compile-time error.

Unison provides syntactic sugar for match expressions in which the scrutinee is the sole argument of a lambda expression:

cases
  pattern_1 -> block_1
  pattern_2 -> block_1pattern_n -> block_n

-- equivalent to
e -> match e with
  pattern_1 -> block_1
  pattern_2 -> block_1pattern_n -> block_n

Apatternhas one of the following forms:

Blank patterns

Ablank patternhas the form_.It matches any expression without creating a variable binding.

For example:

_ = 42 "Always matches"
"Always matches"

Literal patterns

Aliteral patternis a literalBoolean,Nat,Int,Char,orText.A literal pattern matches if the scrutinee has that exact value.

For example:

match 2 Nat.+ 2 with 4 -> "Matches" _ -> "Doesn't match"
"Matches"

Variable patterns

Avariable patternis aregular identifierand matches any expression. The expression that it matches will be bound to that identifier as a variable in the match body.

For example:

match 1 + 1 with
  x -> x + 1

As-patterns

Anas-patternhas the formv@pwherevis aregular identifierandpis a pattern. This pattern matches ifpmatches, and the variablevwill be bound in the body to the value matchingp.

For example, this expression evaluates to3:

matchExpression : Nat
matchExpression = match 1 Nat.+ 1 with
  x@4 -> x Nat.* 2
  y@2 -> y Nat.+ 1
  _   -> 22

Constructor patterns

Aconstructor patternhas the formC p1 p2 … pnwhereCis the name of a data constructor in scope, andp1throughpnare patterns such thatnis thearityofC.Note thatnmay be zero. This pattern matches if the scrutinee reduces to a fully applied invocation of the data constructorCand the patternsp1throughpnmatch the arguments to the constructor.

For example, this expression usesSomeandNone,the constructors of theOptionaltype, to return the 3rd element of the listxsif present or0if there was no 3rd element.

xs = [0, 2, 3, 4, 5] match List.at 3 xs with None -> 0 Some x -> x
4

List patterns

Alist patternmatches aList tfor some typetand has one of four forms:

  1. head List.+: tailmatches a list with at least one element. The patternheadis matched against the first element of the list andtailis matched against the suffix of the list with the first element removed.
  2. init List.:+ lastmatches a list with at least one element. The patterninitis matched against the prefix of the list with the last element removed, andlastis matched against the last element of the list.
  3. Aliteral list patternhas the form[p1, p2, … pn]wherep1throughpnare patterns. The patternsp1throughpnare matched against the elements of the list. This pattern only matches if the length of the scrutinee is the same as the number of elements in the pattern. The pattern[]matches the empty list.
  4. part1 List.++ part2matches a list which composed of the concatenation ofpart1andpart2.At least one ofpart1orpart2must be a pattern with a known list length, otherwise it's unclear where the list is being split. For instance,[x, y] List.++ restis okay as isstart List.++ [x, y],but justa ++ bis not allowed.

Examples:

first : [a] -> Optional a
first = cases
  h +: _ -> Some h
  []     -> None
last : [a] -> Optional a
last = cases
  _ :+ l -> Some l
  []     -> None
exactlyOne : [a] -> Boolean
exactlyOne = cases
  [_] -> true
  _   -> false
lastTwo : [a] -> Optional (a, a)
lastTwo = cases
  start ++ [a, a2] -> Some (a, a2)
  _                -> None
firstTwo : [a] -> Optional (a, a)
firstTwo = cases
  [a, a2] ++ rest -> Some (a, a2)
  _               -> None

Tuple patterns

Atuple patternhas the form(p1, p2, … pn)wherep1throughpnare patterns. The pattern matches if the scrutinee is a tuple of the samearityas the pattern andp1throughpnmatch against the elements of the tuple. The pattern(p)is the same as the patternp,and the pattern()matches the literal value()of the trivial type {()} (both pronounced “unit”).

For example, this expression evaluates to4:

(a, _, c) = (1, 2, 3) a Nat.+ c
4

Ability patterns (orRequestpatterns)

Anability patternonly appears in an ability handler and has one of two forms (seeAbilities and ability handlersfor details):

  1. {C p1 p2 … pn -> k}whereCis the name of an ability constructor in scope, andp1throughpnare patterns such thatnis the arity ofC.Note thatnmay be zero. This pattern matches if the scrutinee reduces to a fully applied invocation of the ability constructorCand the patternsp1throughpnmatch the arguments to the constructor. The scrutinee must be of typeRequest A Tfor some ability{A}and typeT.The variablekwill be bound to the continuation of the program. If the scrutinee has typeRequest A TandChas typeX ->{A} Y,thenkhas typeY -> {A} T.
  2. {p}wherepis a pattern. This matches the case where the computation ispure(the value of typeRequest A Tcalls none of the constructors of the ability{A}).A pattern match on aRequestis not complete unless this case is handled.

See the section onabilities and ability handlersfor examples of ability patterns.

Guard patterns

Aguard patternhas the formp | gwherepis a pattern andgis a Boolean expression that may reference any variables bound inp.The pattern matches ifpmatches andgevaluates totrue.

For example, the following expression evaluates to6:

match 1 + 2 with
  x
    | x == 4     -> 0
    | x + 1 == 4 -> 6
  _              -> 42