Pattern Matching
Pattern expressions#
Entering a pattern#
Once certain expressions in the Erlang AST is encountered, the language enters a pattern matching mode. While in this mode, all expressions in the subtree will no longer be interpreted as normal Erlang expressions, but rather as pattern matches.
This will be referred to as entering a pattern expression.
As a simple example of this, the following expression may have a separate meaning depending on where in the language it is encountered:
If encountered as a normal Erlang expression, this will construct a list of three integers:
However, if this is encountered in a patttern expression, the meaning is completely different. It will now instead match a list with three constant integers in it:
Pattern occurrences#
Entering a pattern expression occurs in the following places of the Erlang language:
- The arguments of any function definition.
- The pattern of a
caseexpression. - Several locations within a
tryexpression.- The body of the
tryexpression, it matches on the return value of the tried expression. - The catch body of the
tryexpression, it matches on the error thrown by the expression.
- The body of the
- Within the body of a
receiveexpression. - To the left of a
=operator. This is left associative, in a chain of severala = b = c, only the rightmostcis a normal expression. This may not have a guard. - In a generator within a list or binary comprehension. This may not have a guard.
There is also one use that doesn't conform to the normal pattern matching rules that apply everywhere else:
- In a binary generator within a list or binary comprehension. This may not have a guard. This looks like a binary pattern, but actually conforms to different rules than you would expect a pattern to normally. See Comprehensions for more details.
Valid expressions in a pattern#
The following expression types are handled as a recursive pattern, any subexpressions are also handled as patterns:
- A tuple.
{Pat, Pat, Pat} - A list.
[Pat, Pat | Pat]
TODO
Matching a pattern#
info
How patterns are matched are described semantically, but this is not how a compiler would actually implement pattern matching. Realistically a more optimized approach is used, which has identical semantics to what is described below.
A pattern will be compared against a term, and the runtime needs to make a decision as to whether the pattern matches the term or not.
In order to determine if a match has occurred, several checks will be performed in order.
- The structure of the pattern is checked against the term. This includes checking the actual types, tuple arities, that all map keys which are matched on are present and more. This is done recursively between the term and the pattern.
- Any variables named within the pattern are handled. This is done in one of several ways:
- If the variable name is not bound in the current scope, then a new binding is created with the value extracted from the term.
- If the variable name has been bound previously in the same pattern, a new guard is inserted which checks equality between the previous pattern value and the current one.
- If the variable name is already bound in the outer scope, a new guard is inserted which checks equality between the value from the term and the value bound in the outer scope.
- Guards are checked.
Self referential patterns#
There are two cases where a pattern may refer to values from itself:
Map key lookup.
In the case of map key lookup, scoping is handled in a purely lexical way. Bindings from an outer scope may be referenced, but bindings from within sibling subtrees may not.
Valid:
Invalid:
Binary pattern matching.
In the case of a binary pattern match, bindings from an outer scope or from earlier in the same binary pattern may be referenced.
Valid:
Invalid:
List of patterns#
In an expressions where several patterns are provided, the are attempted matched in linear order from top to bottom.
A simple example here is the case expression:
The first clause is attempted first, then the second, then the third and then the fourth.
The third clause can never be matched since a previous clause will always match first.