Concurrent programming in Erlang

2.2. Pattern Matching

Patterns have the same structure as terms, with the addition that they can include variables. Variables start with an upper-case letter.

Examples of patterns:

{A, a, 12, [12,34|{a}]}
{A, B, 23}
{x, {X_1}, 12, My_cats_age}
[]

In the above A, B, X_1, and My_cats_age are variables.

Pattern matching provides the basic mechanism by which values become assigned to variables. A variable whose value has been assigned is said to be bound – otherwise it is said to be unbound. The act of assigning a value to a variable is called binding. Once a variable has been bound its value can never be changed. Such variables are called bind once or single assignment. This contrasts with conventional imperative languages which have destructive assignment.

A pattern and a term are said to match if the pattern and term are structurally isomorphic and if, whenever an atomic data type is encountered in the pattern, the same atomic data type is encountered at the same position in the corresponding term. In the case where the pattern contains an unbound variable, the variable is bound to the corresponding element in the term. If the same variable occurs more than once in the pattern then all items occurring at corresponding positions in the term must be identical.

Pattern matching occurs:

  • when evaluating an expression of the form Lhs = Rhs
  • when calling a function
  • when matching a pattern in a case or receive primitive.

2.2.1. Pattern = Expression

The expression Pattern = Expression causes Expression to be evaluated and the result matched against Pattern. The match either succeeds or fails. If the match succeeds any variables occurring in Pattern become bound.

In the following we assume that the pattern matching always succeeds. The treatment of failure will be discussed in detail in a following Chapter.

Examples:

{A, B} = {12, apple}

succeeds with the bindings A → 12^3 and, B → apple.

{C, [Head|Tail]} = {{222, man}, [a,b,c]}

succeeds with the bindings C → {222, man}, Head → a and, Tail → [b, c].

[{person, Name, Age, _}|T] =
        [{person, fred, 22, male},
        {person, susan, 19, female}, ...]

succeeds with the bindings T → [{person, susan, 19, female}, ...]}, Name → fred and Age → 22. In the last example we made use of the anonymous variable written _ – anonymous variables are used when the syntax requires a variable but we are not interested in its value.

If a variable occurs more than once in a pattern then the match will only suc- ceed if the corresponding elements being matched have the same value. So, for example, {A, foo, A} = {123, foo, 123} succeeds, binding A to 123, whereas {A, foo, A} = {123, foo, abc} fails since we cannot simultaneously bind A to 123 and abc.

= is regarded as an infix right associative operator. Thus A = B = C = D is parsed as A = (B = (C = D)). This is probably only useful in a construction like {A, B} = X = ... where we want both the value of an expression and its constituents. The value of the expression Lhs = Rhs is defined to be Rhs.

2.2.2. Pattern matching when calling a function

Erlang provides choice and flow of control through pattern matching. For ex- ample, Program 2.1 defines a function classify_day/1, which returns weekEnd if called with argument saturday or sunday, or it returns weekDay otherwise.

-module(dates).
-export([classify_day/1]).

classify_day(saturday) -> weekEnd;
classify_day(sunday)   -> weekEnd;
classify_day(_)        -> weekDay.

When a function is evaluated, the arguments of the function are matched against the patterns occurring in the function definition. When a match occurs the code following the -> symbol is evaluated, so:

> dates:classify_day(saturday).
weekEnd
> dates:classify_day(friday).
weekDay

The function call is said to fail if none of its clauses match (failure causes the error-trapping mechanisms described later in this book to be used).

Any variables occurring in the patterns describing the different clauses of a function become bound when a particular clause in a function is entered. So, for example, evaluating math3:area({square, 5}) causes the variable Side to be bound to 5.