On this page:
match
matches
matches
_
_
where
where
8.17.0.6

5.2 Matching🔗ℹ

expression

match target_expr:

  maybe_arity_decl

| values_bind:

    result_body

    ...

| ...

 

expression

match target_expr:

  maybe_arity_decl

| values_bind:

    result_body

    ...

| ...

| ~else:

    result_body

    ...

 

expression

match target_expr:

  maybe_arity_decl

| values_bind:

    result_body

    ...

| ...

| ~else result_expr

 

maybe_arity_decl

 = 

~arity: given_arity_num

 | 

~arity given_arity_num

 | 

ϵ

Tries matching the results of target_expr against each values_bind in sequence, and as soon as one matches, returns the result of the corresponding result_body block. The keyword ~else can be used as a synonym for a pattern that always matches without binding any identifiers in the last clause.

Typically, a bind in values_bind imposes requirement on a value and binds some number of identifiers as a result of a successful match. For example, a literal number works as a bind pattern, but it binds zero identifiers. An identifier as a bind pattern matches any value and binds the identifier to the matching value. A list form is a bind pattern with subpatterns as its elements, and it matches a list with the right number of elements that match the corresponding pattern. A when or unless can impose a side condition on a match. The set of bind forms is extensible, so it cannot be completely enumerated here.

> match 1+2

  | 3: "three"

  | ~else: "not three"

"three"

> match [1+2, 3+4]

  | [x, y]: x+y

10

> match 'go ~slow'

  | 'go ~fast': "ok"

match: expected the literal #:fast

> match 1+2

  | n when n > 4: "ok"

match: no matching case

If no values_bind matches the values and there is no ~else clause, a run-time exception is thrown. In that case, when all of the values_bind forms are single-value syntax-object patterns, the generated exception’s message may be specialized to report the expected pattern, instead of just reporting that no cases matched.

> match 1+2

  | 4: "four"

match: no matching case

> match '1+2'

  | '4': "four"

match: expected the literal 4

If an initial segment of single-value values_bind patterns are “literal-like” or combinations of such patterns with ||, then the match is implemented as a case dispatch, and a match is found with logarithmic rather than linear time complexity in the number of literals. “Literal-like” patterns include (usually implicitly used) #%literal, #', Char, and Byte. The remaining patterns are handled as usual.

fun classify_efficiently(n):

  match n

  | 1: "one"

  | 2: "two"

  | 3 || 4: "some"

  | ~else: "more"

Since each clause is allowed to use a multiple-value values_bind pattern, target_expr is also allowed to produce multiple values. All clauses in a match form must match the same number of values, otherwise a syntax error is reported. When no clause or only ~else clause is present, target_expr is expected to produce one value, but this can be overridden by an optional maybe_arity_decl declaration. The given_arity_num number must be a literal nonnegative integer.If a macro expands into a match form that matches a known number of multiple values, and the expansion can produce no clauses, it is recommended that you should explicitly include the maybe_arity_decl, so that a proper fallthrough error is thrown instead of an arity error.

> match values(1, 2)

  | (1, 1): "both one"

  | (1, 2): "one and two"

  | (2, 2): "both two"

"one and two"

> match 1

  | ~else: "one value"

"one value"

> match 1

  | _: "one value"

"one value"

> match 1:

    ~arity: 2

  | ~else: "two values"

result arity mismatch;

 expected number of values not received

  expected: 2

  received: 1

  in: local-binding form

  arguments...:

   1

> match values(1, 2):

    ~arity: 2

  | ~else: "two values"

"two values"

> match values(1, 2)

  | (_, _): "two values"

"two values"

> match values(1, 2)

  | ~else: "one value"

result arity mismatch;

 expected number of values not received

  expected: 1

  received: 2

Static information for target_expr is propagated to each values_bind.

> use_static

> match (["apple", "banana"] :: List.of(String))

  | [s, ...]: [s.length(), ...]

[5, 6]

> match values(["apple", "banana"] :: List.of(String),

               [1, 2] :: List.of(Int))

  | ([s, ...], [i, j]):

      values([s.length(), ...], i < j)

[5, 6]

#true

expression

expr matches bind

 

repetition

repet matches bind

 

expression

expr !matches bind

 

repetition

repet !matches bind

 

~order: equivalence

Produces #true if the value of expr matches bind, #false otherwise. Equivalent to expr is_a matching(bind). The operator combination !matches inverts the test. Either form works as a repetition given a repetition to match.

> [1, 2, 3] matches [_, _, _]

#true

> [1, 2, 3] is_a matching([_, _, _])

#true

binding operator

_

Matches any value without binding any identifiers.

> match 1+2

  | 0: "zero"

  | _: "nonzero"

"nonzero"

expression

_

As an expression by itself, _ is a syntax error, but _ is recognized by #%parens and #%call in expression-like positions to trigger a function-shorthand conversion.

The #%parens conversion applies when () are used in an expression position, and at least one _ appears immediately within the parentheses as in (term ... _ term ...). An immediate _ is one that is not more deeply nested in a term within the parentheses. A single immediate _ is replaced by a fresh identifier id as an expression, and then the parenthesized sequence is placed in the body of a function that uses the identifier as an argument, as in (fun (id): term ... id term ...). If multiple immediate _s are present, each one is replaced by a distinct identifier, the identifiers are used as the arguments of the generated function, and the arguments match the order of the corresponding _. Moreover, static information is gathered from the parenthesized sequence, by parsing it as an expression, and propagated as result information.

> def subtract = (_ - _)

> subtract(2, 1)

1

> [1, 2, 3].map((_ + 1))

[2, 3, 4]

The #%call conversion applies when () are used after an expression, at least one _ appears by itself as an argument, and no argument uses & or is a repetition followed by a .... Each _ argument is converted as in the #%parens conversion, but the conversion applies only for a _ that appears by itself in the group for function-call argument. Moreover, result information is gathered from the inner function and propagated to the outer function.

> def low_bits = bits.field(_, 0, 3)

> low_bits(15)

7

> [1.25, 2.75, 3.0].map(math.round(_))

[1.0, 3.0, 3.0]

A _ combined with other terms as a function-call argument does not trigger a function-shorthand conversion.

> [1, 2, 3].map(_ + 1)

_: not allowed as an expression;

 only allowed as binding pattern or anonymous-function argument,

 and use isn’t an allowed position to create a function

binding operator

left_bind where right_bind = expr

 

binding operator

left_bind where:

  right_bind_and_expr

  ...

 

right_bind_and_expr

 = 

right_bind = expr

 | 

right_bind:

  body

  ...

Creates a binding that matches only when both left_bind matches and when each right_bind matches the value of the corresponding expr or body sequence. Names bound by left_bind and each right_bind are visible in exprs and body sequences for subsequent right_binds.

The where form should generally be used with parentheses around it, since parsing is otherwise likely to interact badly with the enclosing context, such as conflicting interpretations of a = by where and def.

See also when and unless.

> def ([x, y, z] where sum = x+y+z) = [1, 2, 3]

> '$x $y $z'

'1 2 3'

> sum

6

fun

| f(([x, y, z] where sum = x+y+z) when sum == 6):

    '$x $y $z = $sum'

| f(_):

    "something else"

> f([1, 2, 3])

'1 2 3 = 6'

> f([0, 2, 3])

"something else"

> def ([x, y, z] where:

         partial_sum = x+y

         sum = partial_sum+z):

    [1, 2, 3]

> sum

6

> def ([xs] where [x, ...] = xs) = [[1, 2, 3]]

> '$x ...'

'1 2 3'

> def ([xs] where [x, ...] = xs) = ["oops"]

def: value does not satisfy annotation

  value: ["oops"]

  position: 1st

  annotation: matching(List(_)) where ....

expression

where

 

expression

expr where

As an expression operator, where always reports a syntax error.