13.1 Patterns
The full syntax of Cases is presented in figure 13.1. Identifiers in
patterns are either constructor names or variables. Any identifier
that is not the constructor of an inductive or coinductive type is
considered to be a variable. A variable name cannot occur more than
once in a given pattern. It is recommended to start variable names by
a lowercase letter.
If a pattern has the form (c x) where c is a constructor
symbol and x is a linear vector of variables, it is called
simple: it is the kind of pattern recognized by the basic
version of Cases. If a pattern is
not simple we call it nested.
A variable pattern matches any value, and the identifier is bound to
that value. The pattern ``_'' (called ``don't care'' or
``wildcard'' symbol) also matches any value, but does not bind anything. It
may occur an arbitrary number of times in a pattern. Alias patterns
written (pattern as identifier) are
also accepted. This pattern matches the same values as pattern
does and identifier is bound to the matched value. A list of
patterns is also considered as a pattern and is called multiple
pattern.
Notice also the annotation is mandatory when the sequence of equation is
empty.
nested_pattern |
:= |
ident |
|
| |
_ |
|
| |
( ident nested_pattern ... nested_pattern ) |
|
| |
( nested_pattern as ident ) |
|
| |
( nested_pattern , nested_pattern ) |
|
| |
( nested_pattern ) |
|
|
|
mult_pattern |
:= |
nested_pattern ... nested_pattern |
|
|
|
ext_eqn |
:= |
mult_pattern => term |
|
|
|
term |
:= |
[annotation] Cases term ... term of
[ext_eqn | ... | ext_eqn] end |
Figure 13.1: Extended Cases syntax
Since extended Cases expressions are compiled into the primitive
ones, the expressiveness of the theory remains the same. Once the
stage of parsing has finished only simple patterns remain. An easy way
to see the result of the expansion is by printing the term with
Print if the term is a constant, or using the command
Check.
The extended Cases still accepts an optional elimination
predicate enclosed between brackets <>. Given a pattern
matching expression, if all the right hand sides of => (rhs in short) have the same type, then this type can be sometimes
synthesized, and so we can omit the <>. Otherwise
the predicate between <> has to be provided, like for the basic
Cases.
Let us illustrate through examples the different aspects of extended
pattern matching. Consider for example the function that computes the
maximum of two natural numbers. We can write it in primitive syntax
by:
Coq < Fixpoint max [n,m:nat] : nat :=
Coq < Cases n of
Coq < O => m
Coq < | (S n') => Cases m of
Coq < O => (S n')
Coq < | (S m') => (S (max n' m'))
Coq < end
Coq < end.
max is recursively defined
Using multiple patterns in the definition allows to write:
Coq < Reset max.
Coq < Fixpoint max [n,m:nat] : nat :=
Coq < Cases n m of
Coq < O _ => m
Coq < | (S n') O => (S n')
Coq < | (S n') (S m') => (S (max n' m'))
Coq < end.
max is recursively defined
which will be compiled into the previous form.
The pattern-matching compilation strategy examines patterns from left
to right. A Cases expression is generated only when
there is at least one constructor in the column of patterns. E.g. the
following example does not build a Cases expression.
Coq < Check [x:nat]<nat>Cases x of y => y end.
[x:nat]x
: nat->nat
We can also use ``as patterns'' to associate a name to a
sub-pattern:
Coq < Reset max.
Coq < Fixpoint max [n:nat] : nat -> nat :=
Coq < [m:nat] Cases n m of
Coq < O _ => m
Coq < | ((S n') as p) O => p
Coq < | (S n') (S m') => (S (max n' m'))
Coq < end.
max is recursively defined
Here is now an example of nested patterns:
Coq < Fixpoint even [n:nat] : bool :=
Coq < Cases n of
Coq < O => true
Coq < | (S O) => false
Coq < | (S (S n')) => (even n')
Coq < end.
even is recursively defined
This is compiled into:
Coq < Print even.
even =
Fix even
{even [n:nat] : bool :=
Cases n of
O => true
| (S n0) => Cases n0 of
O => false
| (S n') => (even n')
end
end}
: nat->bool
In the previous examples patterns do not conflict with, but
sometimes it is comfortable to write patterns that admit a non
trivial superposition. Consider
the boolean function lef that given two natural numbers
yields true if the first one is less or equal than the second
one and false otherwise. We can write it as follows:
Coq < Fixpoint lef [n,m:nat] : bool :=
Coq < Cases n m of
Coq < O x => true
Coq < | x O => false
Coq < | (S n) (S m) => (lef n m)
Coq < end.
lef is recursively defined
Note that the first and the second multiple pattern superpose because
the couple of values O O matches both. Thus, what is the result
of the function on those values? To eliminate ambiguity we use the
textual priority rule: we consider patterns ordered from top to
bottom, then a value is matched by the pattern at the ith row if and
only if it is not matched by some pattern of a previous row. Thus in the
example,
O O is matched by the first pattern, and so (lef O O)
yields true.
Another way to write this function is:
Coq < Reset lef.
Coq < Fixpoint lef [n,m:nat] : bool :=
Coq < Cases n m of
Coq < O x => true
Coq < | (S n) (S m) => (lef n m)
Coq < | _ _ => false
Coq < end.
lef is recursively defined
Here the last pattern superposes with the first two. Because
of the priority rule, the last pattern
will be used only for values that do not match neither the first nor
the second one.
Terms with useless patterns are not accepted by the
system. Here is an example:
Coq < Check [x:nat]Cases x of O => true | (S _) => false | x => true end.
Toplevel input, characters 53-62
> Check [x:nat]Cases x of O => true | (S _) => false | x => true end.
> ^^^^^^^^^
Error: This clause is redundant