2 Diving In
2.5 Pattern Matching
2.5.1 What Are Patterns?
Pattern matching in Erlang is huge, and it has a proportional impact on LFE and
what one can do with this dialect of Lisp. Pattern matching in LFE can be used
in function clauses, let
, case
, receive
and in the macros
cond
, lc
, and bc
. From the REPL, pattern matching may be done
in set
as well.
Pattern matching in LFE happens when an expression matches a given pattern, e.g.:
where the <pattern>
might be something like this:
or this:
or this:
or this:
and the <expression>
is any legal LFE expression. Ideally, it will return
data that will be matched by the pattern.
If the matching succeeds, any unbound variables in the pattern become bound. If the matching fails, a run-time error occurs. All of this is best understood through the examples given below. Each example is preceeded by the general form of pattern as used in the given context. This should help keep things clear, even when the examples get convoluted.
2.5.2 Patterns in Forms
2.5.2.1 let
Pattern matching in let
has the following general form:
Examples:
In this example, we have a pattern of (tuple len status data)
and this is
getting matched against our expression which is some data of the form
#(8 ok "Trillian")
. The pattern expects a tuple, and a tuple is what we
gave it. With the pattern's variables bound inside the let
, we can return
a list of the variables.
If our pattern was written to expect a list and the expression was a tuple,
we'd get a badmatch
error:
Whatever our expression is going to be needs to be matched in the pattern. If
we had a list integers in the expression, we would need a pattern like
(list i1 i2 i3 ...)
.
Here's a super-simplified version of a let
with pattern matching:
Here our pattern was simply the variable data
and our expression was the
string "Trillian". This, of course, is easily recognized as a standard variable
assignment within a let
.
Patterns can nest, though, and with this you can start to get a sense of the power they hold. Let's look at a more complicated example:
As you can see, we've nested our expression: length is a two-valued list and status is a two-valued tuple. Our pattern, however, is still simple. But this is going to change: we want to extract our data into more variables, and we do this by mirroring the expression data structure in the pattern itself:
As you can see, our nested pattern extracted the data into the pattern's variables. If all we cared about was the status message, we could make this simpler by using the "I don't care" variable (the underscore):
Having seen these examples, you are probably gaining some insight into the power of pattern matching in Erlang and LFE. There's more, though :-) See below for equally potent uses.
2.5.2.2 case
Pattern matching in case
has the following general form:
Keep in mind that case
may also be used (optionally) inside the try
form. For more information on try
, see
section 5.2.
Let's take a look at case
in action:
The patterns we are using in this case
example expect data of one
particular format, differentiating by the second element of the provided tuple.
With new data, we can exercise the other cases:
We won't re-type the case
example here; just hit the "up" arror until you
get to the case
entry and hit return:
Similarly, we can test the remaining case:
2.5.2.3 receive
Pattern matching in receive
has the following general form:
There is a tutorial on working with Erlang's light weight processes in LFE, and
several example usages of receive
are given there. On the second page of
that tutorial, we see that any message sent to receive
is accepted and
processed. In the example below, we replace the simple pattern of the whole
data (i.e., msg
) with a series of patterns that will print only if the
message matches one of the provided patterns.
Save the following in a file named rcv-pttrn.lfe
:
Next, start up the LFE REPL, compile the module above, and start our safety server:
Now let's give our patterns a try by sending messages to the server process:
As you can see, the receive
patterns are working.
We can also see what happens when we send messages that don't match any of the defined patterns:
Absolutely nothing, that's what. Well, nothing from the process we spawned, that is... just the REPL doing its thang.
2.5.2.4 cond
Pattern matching in cond
has the following general form:
Typically, a cond
looks like this:
In other words, a series of tests with conditional results. LFE extends the basic form with support for pattern matching, as seen in the general form above.
Here's an example of how one can do pattern matching in LFE with cond
(starting with the setting of some data):
Note that this is a replacement of the case
example above.
We can set the data
variable differently to exercise the other code
paths, and then enter the cond
expression from above (elided below to
save space):
2.5.3 Special Cases
2.5.3.1 set
in the REPL
Using set
in the REPL has the following general form:
Note that set
is only valid when running the LFE shell. Example usage:
2.5.3.2 Aliases with =
Aliases are defined with the following general form:
Aliases can be used anywhere in a pattern. A quick example of this, updating the previous example with aliases:
The same variables that were bound in the previous example are bound in this one:
In addition, however, we have aliased new variables to these:
2.5.3.3 Arguments to defun
Pattern matching in functions has the following general form:
We haven't covered functions yet (that's coming up in Chapter 4), so this will be a short preview focusing just on the pattern usage in functions, with more detail coming later.
Proper functions can't be defined in the LFE REPL, so save the following to
func-pttrn.lfe
:
As you can see, the usual function arguments have been replaced with a pattern.
In particular, this function will accept any of three options with two
arguments each: where the first argument is 'ok
, or where it is
'warn
, or where it is 'crit
.
Let's compile our new module from the LFE REPL:
Now let's step it through its paces:
If a pattern is not matched in our example (which has no fallback pattern), an error is raised:
2.5.3.4 Arguments to Anonymous Functions
One can use patterns in arguments with anonymous functions similarly to how one
does with named functions, demonstrated above. In LFE, this is done with
match-lambda
. Here's an example done in the REPL:
Usage is similar as well:
2.5.3.5 Patterns in Comprehensions
List and binary comprehensions make use of patterns in a limited sense. They have the following general forms:
and
where the guard
in both cases is optional.
You can read more about LFE comprehensions in section 3.3