Tutorial COQ
Tutorial COQ
A Tutorial
December 16, 2013
Version 8.4pl31
unix:~> coqtop
Welcome to Coq 8.2 (January 2009)
Coq <
The first line gives a banner stating the precise version of C OQ used. You
1 Alternative graphical interfaces exist: Proof General and Pcoq.
3
4
should always return this banner when you report an anomaly to our bug-tracking
system http://logical.futurs.inria.fr/coq-bugs
Chapter 1
1.1.1 Declarations
A declaration associates a name with a specification. A name corresponds roughly
to an identifier in a programming language, i.e. to a string of letters, digits, and a
few ASCII symbols like underscore (_) and prime (’), starting with a letter. We use
case distinction, so that the names A and a are distinct. Certain strings are reserved
as key-words of C OQ, and thus are forbidden as user identifiers.
A specification is a formal expression which classifies the notion which is being
declared. There are basically three kinds of specifications: logical propositions,
mathematical collections, and abstract types. They are classified by the three basic
sorts of the system, called respectively Prop, Set, and Type, which are themselves
atomic abstract types.
Every valid expression e in Gallina is associated with a specification, itself a
valid expression, called its type τ(E). We write e : τ(E) for the judgment that e is
of type E. You may request C OQ to return to you the type of a valid expression by
using the command Check:
5
6 CHAPTER 1. BASIC PREDICATE CALCULUS
Thus we know that the identifier O (the name ‘O’, not to be confused with
the numeral ‘0’ which is not a proper identifier!) is known in the current context,
and that its type is the specification nat. This specification is itself classified as a
mathematical collection, as we may readily check:
The specification Set is an abstract type, one of the basic sorts of the Gal-
lina language, whereas the notions nat and O are notions which are defined in the
arithmetic prelude, automatically loaded when running the C OQ system.
We start by introducing a so-called section name. The role of sections is to
structure the modelisation by limiting the scope of parameters, hypotheses and
definitions. It will also give a convenient way to reset part of the development.
With what we already know, we may now enter in the system a declaration, corre-
sponding to the informal mathematics let n be a natural number.
Indeed we may check that the relation gt is known with the right type in the
current context:
which may be composed one more times with nat in order to obtain the type
nat->nat->Prop of binary relations over natural numbers. Actually the type
nat->nat->Prop is an abbreviation for nat->(nat->Prop).
Functional notions may be composed in the usual way. An expression f of
type A → B may be applied to an expression e of type A in order to form the ex-
pression ( f e) of type B. Here we get that the expression (gt n) is well-formed
of type nat->Prop, and thus that the expression (gt n O), which abbreviates
((gt n) O), is a well-formed proposition.
1.1.2 Definitions
The initial prelude contains a few arithmetic definitions: nat is defined as a math-
ematical collection (type Set), constants O, S, plus, are defined as objects of types
respectively nat, nat->nat, and nat->nat->nat. You may introduce new defini-
tions, which link a name to a well-typed value. For instance, we may introduce the
constant one as being defined to be equal to the successor of zero:
This introduces the constant double defined as the expression fun m:nat =>
plus m m. The abstraction introduced by fun is explained as follows. The ex-
pression fun x:A => e is well formed of type A->B in a context whenever the
expression e is well-formed of type B in the given context to which we add the
declaration that x is of type A. Here x is a bound, or dummy variable in the ex-
pression fun x:A => e. For instance we could as well have defined double as
fun n:nat => (plus n n).
Bound (local) variables and free (global) variables may be mixed. For instance,
we may define the function which adds the constant n to its argument as
8 CHAPTER 1. BASIC PREDICATE CALCULUS
However, note that here we may not rename the formal argument m into n without
capturing the free occurrence of n, and thus changing the meaning of the defined
notion.
Binding operations are well known for instance in logic, where they are called
quantifiers. Thus we may universally quantify a proposition such as m > 0 in order
to get a universal proposition ∀m · m > 0. Indeed this operator is available in C OQ,
with the following syntax: forall m:nat, gt m O. Similarly to the case of the
functional abstraction binding, we are obliged to declare explicitly the type of the
quantified variable. We check:
Coq < Check (forall m:nat, gt m 0).
forall m : nat, m > 0
: Prop
We may clean-up the development by removing the contents of the current section:
Coq < Reset Declaration.
Let us now embark on a simple proof. We want to prove the easy tautology
((A → (B → C)) → (A → B) → (A → C). We enter the proof engine by the com-
mand Goal, followed by the conjecture we want to verify:
Coq < Goal (A -> B -> C) -> (A -> B) -> A -> C.
1 subgoal
1.2. INTRODUCTION TO THE PROOF ENGINE: MINIMAL LOGIC 9
A : Prop
B : Prop
C : Prop
============================
(A -> B -> C) -> (A -> B) -> A -> C
The system displays the current goal below a double line, local hypotheses
(there are none initially) being displayed above the line. We call the combination of
local hypotheses with a goal a judgment. We are now in an inner loop of the system,
in proof mode. New commands are available in this mode, such as tactics, which
are proof combining primitives. A tactic operates on the current goal by attempting
to construct a proof of the corresponding judgment, possibly from proofs of some
hypothetical judgments, which are then added to the current list of conjectured
judgments. For instance, the intro tactic is applicable to any judgment whose
goal is an implication, by moving the proposition to the left of the application to
the list of local hypotheses:
Coq < intro H.
1 subgoal
A : Prop
B : Prop
C : Prop
H : A -> B -> C
============================
(A -> B) -> A -> C
A : Prop
B : Prop
C : Prop
H : A -> B -> C
H’ : A -> B
HA : A
============================
C
We notice that C, the current goal, may be obtained from hypothesis H, provided
the truth of A and B are established. The tactic apply implements this piece of
reasoning:
Coq < apply H.
2 subgoals
A : Prop
B : Prop
C : Prop
10 CHAPTER 1. BASIC PREDICATE CALCULUS
H : A -> B -> C
H’ : A -> B
HA : A
============================
A
subgoal 2 is:
B
We are now in the situation where we have two judgments as conjectures that
remain to be proved. Only the first is listed in full, for the others the system displays
only the corresponding subgoal, without its local hypotheses list. Remark that
apply has kept the local hypotheses of its father judgment, which are still available
for the judgments it generated.
In order to solve the current goal, we just have to notice that it is exactly avail-
able as hypothesis HA:
Coq < exact HA.
1 subgoal
A : Prop
B : Prop
C : Prop
H : A -> B -> C
H’ : A -> B
HA : A
============================
B
Now H 0 applies:
Coq < apply H’.
1 subgoal
A : Prop
B : Prop
C : Prop
H : A -> B -> C
H’ : A -> B
HA : A
============================
A
And we may now conclude the proof as before, with exact HA. Actually, we
may not bother with the name HA, and just state that the current goal is solvable
from the current local assumptions:
Coq < assumption.
No more subgoals.
The proof is now finished. We may either discard it, by using the command
Abort which returns to the standard C OQ toplevel loop without further ado, or
else save it as a lemma in the current context, under name say trivial_lemma:
1.2. INTRODUCTION TO THE PROOF ENGINE: MINIMAL LOGIC 11
As a comment, the system shows the proof script listing all tactic commands
used in the proof.
Let us redo the same proof with a few variations. First of all we may name the
initial goal as a conjectured lemma:
Coq < Lemma distr_impl : (A -> B -> C) -> (A -> B) -> A -> C.
1 subgoal
A : Prop
B : Prop
C : Prop
============================
(A -> B -> C) -> (A -> B) -> A -> C
Next, we may omit the names of local assumptions created by the introduction
tactics, they can be automatically created by the proof engine as new non-clashing
names.
Coq < intros.
1 subgoal
A : Prop
B : Prop
C : Prop
H : A -> B -> C
H0 : A -> B
H1 : A
============================
C
• T1 ; T2 (read T1 then T2 ) applies tactic T1 to the current goal, and then tactic
T2 to all the subgoals generated by T1 .
• T ; [T1 |T2 |...|Tn ] applies tactic T to the current goal, and then tactic T1 to the
first newly generated subgoal, ..., Tn to the nth.
12 CHAPTER 1. BASIC PREDICATE CALCULUS
We may thus complete the proof of distr_impl with one composite tactic:
Coq < apply H; [ assumption | apply H0; assumption ].
No more subgoals.
Let us now save lemma distr_impl:
Coq < Save.
intros.
apply H; [ assumption | apply H0; assumption ].
distr_impl is defined
Here Save needs no argument, since we gave the name distr_impl in ad-
vance; it is however possible to override the given name by giving a different argu-
ment to command Save.
Actually, such an easy combination of tactics intro, apply and assumption
may be found completely automatically by an automatic tactic, called auto, with-
out user guidance:
Coq < Lemma distr_imp : (A -> B -> C) -> (A -> B) -> A -> C.
1 subgoal
A : Prop
B : Prop
C : Prop
============================
(A -> B -> C) -> (A -> B) -> A -> C
Coq < auto.
No more subgoals.
This time, we do not save the proof, we just discard it with the Abort com-
mand:
At any point during a proof, we may use Abort to exit the proof mode and go
back to Coq’s main loop. We may also use Restart to restart from scratch the
proof of the same lemma. We may also use Undo to backtrack one step, and more
generally Undo n to backtrack n steps.
We end this section by showing a useful command, Inspect n., which in-
spects the global C OQ environment, showing the last n declared notions:
Coq < Inspect 3.
*** [C : Prop]
trivial_lemma : (A -> B -> C) -> (A -> B) -> A -> C
distr_impl : (A -> B -> C) -> (A -> B) -> A -> C
A : Prop
B : Prop
C : Prop
============================
A /\ B -> B /\ A
Coq < intro.
1 subgoal
A : Prop
B : Prop
C : Prop
H : A /\ B
============================
B /\ A
We make use of the conjunctive hypothesis H with the elim tactic, which breaks
it into its components:
Coq < elim H.
1 subgoal
A : Prop
B : Prop
C : Prop
H : A /\ B
============================
A -> B -> B /\ A
We now use the conjunction introduction tactic split, which splits the con-
junctive goal into the two subgoals:
Coq < split.
2 subgoals
14 CHAPTER 1. BASIC PREDICATE CALCULUS
A : Prop
B : Prop
C : Prop
H : A /\ B
H0 : A
H1 : B
============================
B
subgoal 2 is:
A
and the proof is now trivial. Indeed, the whole proof is obtainable as follows:
Coq < Restart.
1 subgoal
A : Prop
B : Prop
C : Prop
============================
A /\ B -> B /\ A
Coq < intro H; elim H; auto.
No more subgoals.
Coq < Qed.
intro H; elim H; auto.
and_commutative is defined
The tactic auto succeeded here because it knows as a hint the conjunction
introduction operator conj
Coq < Check conj.
conj
: forall A B : Prop, A -> B -> A /\ B
1.3.2 Disjunction
In a similar fashion, let us consider disjunction:
A : Prop
1.3. PROPOSITIONAL CALCULUS 15
B : Prop
C : Prop
============================
A \/ B -> B \/ A
Coq < intro H; elim H.
2 subgoals
A : Prop
B : Prop
C : Prop
H : A \/ B
============================
A -> B \/ A
subgoal 2 is:
B -> B \/ A
Let us prove the first subgoal in detail. We use intro in order to be left to
prove B\/A from A:
A : Prop
B : Prop
C : Prop
H : A \/ B
HA : A
============================
B \/ A
subgoal 2 is:
B -> B \/ A
A : Prop
B : Prop
C : Prop
HA : A
============================
B \/ A
subgoal 2 is:
B -> B \/ A
16 CHAPTER 1. BASIC PREDICATE CALCULUS
The disjunction connective has two introduction rules, since P\/Q may be ob-
tained from P or from Q; the two corresponding proof constructors are called re-
spectively or_introl and or_intror; they are applied to the current goal by tac-
tics left and right respectively. For instance:
Coq < right.
2 subgoals
A : Prop
B : Prop
C : Prop
HA : A
============================
A
subgoal 2 is:
B -> B \/ A
Coq < trivial.
1 subgoal
A : Prop
B : Prop
C : Prop
H : A \/ B
============================
B -> B \/ A
The tactic trivial works like auto with the hints database, but it only tries those
tactics that can solve the goal in one step.
As before, all these tedious elementary steps may be performed automatically,
as shown for the second symmetric case:
However, auto alone does not succeed in proving the full lemma, because it
does not try any elimination step. It is a bit disappointing that auto is not able to
prove automatically such a simple tautology. The reason is that we want to keep
auto efficient, so that it is always effective to use.
1.3.3 Tauto
A complete tactic for propositional tautologies is indeed available in C OQ as the
tauto tactic.
Coq < Restart.
1 subgoal
A : Prop
B : Prop
1.3. PROPOSITIONAL CALCULUS 17
C : Prop
============================
A \/ B -> B \/ A
It is not easy to understand the notation for proof terms without a few ex-
planations. The fun prefix, such as fun H:A\/B =>, corresponds to intro H,
whereas a subterm such as (or_intror B H0) corresponds to the sequence of tac-
tics apply or_intror; exact H0. The generic combinator or_intror needs to
be instantiated by the two properties B and A. Because A can be deduced from the
type of H0, only B is printed. The two instantiations are effected automatically by
the tactic apply when pattern-matching a goal. The specialist will of course rec-
ognize our proof term as a λ-term, used as notation for the natural deduction proof
term through the Curry-Howard isomorphism. The naive user of C OQ may safely
ignore these formal details.
Let us exercise the tauto tactic on a more complex example:
A : Prop
B : Prop
C : Prop
============================
A -> B /\ C -> (A -> B) /\ (A -> C)
A : Prop
B : Prop
C : Prop
============================
((A -> B) -> A) -> A
Coq < try tauto.
1 subgoal
A : Prop
B : Prop
C : Prop
============================
((A -> B) -> A) -> A
Note the use of the Try tactical, which does nothing if its tactic argument fails.
This may come as a surprise to someone familiar with classical reasoning.
Peirce’s lemma is true in Boolean logic, i.e. it evaluates to true for every truth-
assignment to A and B. Indeed the double negation of Peirce’s law may be proved
in C OQ using tauto:
Coq < Abort.
Current goal aborted
Coq < Lemma NNPeirce : ~ ~ (((A -> B) -> A) -> A).
1 subgoal
A : Prop
B : Prop
C : Prop
============================
~ ~ (((A -> B) -> A) -> A)
Coq < tauto.
No more subgoals.
Coq < Qed.
tauto.
NNPeirce is defined
In classical logic, the double negation of a proposition is equivalent to this
proposition, but in the constructive logic of C OQ this is not so. If you want to use
classical logic in C OQ, you have to import explicitly the Classical module, which
will declare the axiom classic of excluded middle, and classical tautologies such
as de Morgan’s laws. The Require command is used to import a module from
C OQ’s library:
1.3. PROPOSITIONAL CALCULUS 19
and it is now easy (although admittedly not the most direct way) to prove a
classical law such as Peirce’s:
Coq < Lemma Peirce : ((A -> B) -> A) -> A.
1 subgoal
A : Prop
B : Prop
C : Prop
============================
((A -> B) -> A) -> A
Coq < apply NNPP; tauto.
No more subgoals.
Coq < Qed.
apply NNPP; tauto.
Peirce is defined
rule2 is assumed
Coq < Hypothesis rule3 : Married -> ~ GoOutSunday.
rule3 is assumed
Coq < Hypothesis rule4 : GoOutSunday <-> Scottish.
rule4 is assumed
Coq < Hypothesis rule5 : WearKilt -> Scottish /\ Married.
rule5 is assumed
Coq < Hypothesis rule6 : Scottish -> WearKilt.
rule6 is assumed
Coq < Lemma NoMember : False.
1 subgoal
A : Prop
B : Prop
C : Prop
Scottish : Prop
RedSocks : Prop
WearKilt : Prop
Married : Prop
GoOutSunday : Prop
rule1 : ~ Scottish -> RedSocks
rule2 : WearKilt \/ ~ RedSocks
rule3 : Married -> ~ GoOutSunday
rule4 : GoOutSunday <-> Scottish
rule5 : WearKilt -> Scottish /\ Married
rule6 : Scottish -> WearKilt
============================
False
Coq < tauto.
No more subgoals.
Coq < Qed.
tauto.
NoMember is defined
We shall now declare a new Section, which will allow us to define notions
local to a well-delimited scope. We start by assuming a domain of discourse D, and
a binary relation R over D:
Coq < Section Predicate_calculus.
Coq < Variable D : Set.
D is assumed
Coq < Variable R : D -> D -> Prop.
R is assumed
Remark the syntax forall x:D, which stands for universal quantification ∀x :
D.
D : Set
R : D -> D -> Prop
R_symmetric : forall x y : D, R x y -> R y x
R_transitive : forall x y z : D, R x y -> R y z -> R x z
============================
forall x : D, (exists y : D, R x y) -> R x x
Remark that the hypotheses which are local to the currently opened sections are
listed as local hypotheses to the current goals. The rationale is that these hypotheses
are going to be discharged, as we shall see, when we shall close the corresponding
sections.
Note the functional syntax for existential quantification. The existential quan-
tifier is built from the operator ex, which expects a predicate as argument:
and the notation (exists x:D, P x) is just concrete syntax for the expression
(ex D (fun x:D => P x)). Existential quantification is handled in C OQ in a
similar fashion to the connectives /\ and \/ : it is introduced by the proof combi-
nator ex_intro, which is invoked by the specific tactic Exists, and its elimination
provides a witness a:D to P, together with an assumption h:(P a) that indeed a
verifies P. Let us see how this works on this simple example.
D : Set
R : D -> D -> Prop
R_symmetric : forall x y : D, R x y -> R y x
R_transitive : forall x y z : D, R x y -> R y z -> R x z
x : D
x_Rlinked : exists y : D, R x y
============================
R x x
1.4. PREDICATE CALCULUS 23
Remark that intros treats universal quantification in the same way as the
premises of implications. Renaming of bound variables occurs when it is needed;
for instance, had we started with intro y, we would have obtained the goal:
Coq < intro y.
1 subgoal
D : Set
R : D -> D -> Prop
R_symmetric : forall x y : D, R x y -> R y x
R_transitive : forall x y z : D, R x y -> R y z -> R x z
y : D
============================
(exists y0 : D, R y y0) -> R y y
D : Set
R : D -> D -> Prop
R_symmetric : forall x y : D, R x y -> R y x
R_transitive : forall x y z : D, R x y -> R y z -> R x z
x : D
x_Rlinked : exists y : D, R x y
============================
forall x0 : D, R x x0 -> R x x
Coq < intros y Rxy.
1 subgoal
D : Set
R : D -> D -> Prop
R_symmetric : forall x y : D, R x y -> R y x
R_transitive : forall x y z : D, R x y -> R y z -> R x z
x : D
x_Rlinked : exists y : D, R x y
y : D
Rxy : R x y
============================
R x x
Now we want to use R_transitive. The apply tactic will know how to match
x with x, and z with x, but needs help on how to instantiate y, which appear in the
hypotheses of R_transitive, but not in its conclusion. We give the proper hint to
apply in a with clause, as follows:
Coq < apply R_transitive with y.
2 subgoals
24 CHAPTER 1. BASIC PREDICATE CALCULUS
D : Set
R : D -> D -> Prop
R_symmetric : forall x y : D, R x y -> R y x
R_transitive : forall x y z : D, R x y -> R y z -> R x z
x : D
x_Rlinked : exists y : D, R x y
y : D
Rxy : R x y
============================
R x y
subgoal 2 is:
R y x
D : Set
R : D -> D -> Prop
R_symmetric : forall x y : D, R x y -> R y x
R_transitive : forall x y z : D, R x y -> R y z -> R x z
x : D
x_Rlinked : exists y : D, R x y
y : D
Rxy : R x y
============================
R y x
Coq < apply R_symmetric; assumption.
No more subgoals.
Coq < Qed.
Here C OQ’s printout is a warning that all local hypotheses have been dis-
charged in the statement of refl_if, which now becomes a general theorem in
the first-order language declared in section Predicate_calculus. In this par-
ticular example, the use of section R_sym_trans has not been really significant,
since we could have instead stated theorem refl_if in its general form, and done
basically the same proof, obtaining R_symmetric and R_transitive as local hy-
potheses by initial intros rather than as global hypotheses in the context. But if
we had pursued the theory by proving more theorems about relation R, we would
have obtained all general statements at the closing of the section, with minimal
dependencies on the hypotheses of symmetry and transitivity.
1.4. PREDICATE CALCULUS 25
We shall now prove a well-known fact from first-order logic: a universal pred-
icate is non-empty, or in other terms existential quantification follows from univer-
sal quantification.
D : Set
R : D -> D -> Prop
P : D -> Prop
d : D
============================
(forall x : D, P x) -> exists a : D, P a
Coq < intro UnivP.
1 subgoal
D : Set
R : D -> D -> Prop
P : D -> Prop
d : D
UnivP : forall x : D, P x
============================
exists a : D, P a
First of all, notice the pair of parentheses around forall x:D, P x in the
statement of lemma weird. If we had omitted them, C OQ’s parser would have
interpreted the statement as a truly trivial fact, since we would postulate an x ver-
ifying (P x). Here the situation is indeed more problematic. If we have some
element in Set D, we may apply UnivP to it and conclude, otherwise we are stuck.
Indeed such an element d exists, but this is just by virtue of our new signature.
This points out a subtle difference between standard predicate calculus and C OQ.
In standard first-order logic, the equivalent of lemma weird always holds, because
such a rule is wired in the inference rules for quantifiers, the semantic justifica-
tion being that the interpretation domain is assumed to be non-empty. Whereas in
C OQ, where types are not assumed to be systematically inhabited, lemma weird
only holds in signatures which allow the explicit construction of an element in the
domain of the predicate.
Let us conclude the proof, in order to show the use of the Exists tactic:
26 CHAPTER 1. BASIC PREDICATE CALCULUS
D : Set
R : D -> D -> Prop
P : D -> Prop
d : D
EM : forall A : Prop, A \/ ~ A
============================
exists x : D, P x -> forall x0 : D, P x0
The proof goes by cases on whether or not there is someone who does not drink.
Such reasoning by cases proceeds by invoking the excluded middle principle, via
elim of the proper instance of EM:
Coq < elim (EM (exists x, ~ P x)).
2 subgoals
D : Set
R : D -> D -> Prop
P : D -> Prop
d : D
EM : forall A : Prop, A \/ ~ A
============================
(exists x : D, ~ P x) ->
exists x : D, P x -> forall x0 : D, P x0
subgoal 2 is:
~ (exists x : D, ~ P x) ->
exists x : D, P x -> forall x0 : D, P x0
2 subgoals
D : Set
R : D -> D -> Prop
P : D -> Prop
d : D
EM : forall A : Prop, A \/ ~ A
Non_drinker : exists x : D, ~ P x
Tom : D
Tom_does_not_drink : ~ P Tom
============================
exists x : D, P x -> forall x0 : D, P x0
subgoal 2 is:
~ (exists x : D, ~ P x) ->
exists x : D, P x -> forall x0 : D, P x0
We conclude in that case by considering Tom, since his drinking leads to a
contradiction:
Coq < exists Tom; intro Tom_drinks.
2 subgoals
D : Set
R : D -> D -> Prop
P : D -> Prop
d : D
EM : forall A : Prop, A \/ ~ A
Non_drinker : exists x : D, ~ P x
Tom : D
Tom_does_not_drink : ~ P Tom
Tom_drinks : P Tom
============================
forall x : D, P x
subgoal 2 is:
~ (exists x : D, ~ P x) ->
exists x : D, P x -> forall x0 : D, P x0
There are several ways in which we may eliminate a contradictory case; a sim-
ple one is to use the absurd tactic as follows:
Coq < absurd (P Tom); trivial.
1 subgoal
D : Set
R : D -> D -> Prop
P : D -> Prop
d : D
EM : forall A : Prop, A \/ ~ A
============================
~ (exists x : D, ~ P x) ->
exists x : D, P x -> forall x0 : D, P x0
28 CHAPTER 1. BASIC PREDICATE CALCULUS
We now proceed with the second case, in which actually any person will do;
such a John Doe is given by the non-emptiness witness d:
Coq < intro No_nondrinker; exists d; intro d_drinks.
1 subgoal
D : Set
R : D -> D -> Prop
P : D -> Prop
d : D
EM : forall A : Prop, A \/ ~ A
No_nondrinker : ~ (exists x : D, ~ P x)
d_drinks : P d
============================
forall x : D, P x
Now we consider any Dick in the bar, and reason by cases according to its
drinking or not:
Coq < intro Dick; elim (EM (P Dick)); trivial.
1 subgoal
D : Set
R : D -> D -> Prop
P : D -> Prop
d : D
EM : forall A : Prop, A \/ ~ A
No_nondrinker : ~ (exists x : D, ~ P x)
d_drinks : P d
Dick : D
============================
~ P Dick -> P Dick
The only non-trivial case is again treated by contradiction:
Coq < intro Dick_does_not_drink; absurd (exists x, ~ P x); trivial.
1 subgoal
D : Set
R : D -> D -> Prop
P : D -> Prop
d : D
EM : forall A : Prop, A \/ ~ A
No_nondrinker : ~ (exists x : D, ~ P x)
d_drinks : P d
Dick : D
Dick_does_not_drink : ~ P Dick
============================
exists x : D, ~ P x
Coq < exists Dick; trivial.
No more subgoals.
1.4. PREDICATE CALCULUS 29
Remark how the three theorems are completely generic in the most general
fashion; the domain D is discharged in all of them, R is discharged in refl_if only,
P is discharged only in weird and drinker, along with the hypothesis that D is
inhabited. Finally, the excluded middle hypothesis is discharged only in drinker.
Note that the name d has vanished as well from the statements of weird and
drinker, since C OQ’s pretty-printer replaces systematically a quantification such
as forall d:D, E, where d does not occur in E, by the functional notation D->E.
Similarly the name EM does not appear in drinker.
Actually, universal quantification, implication, as well as function formation,
are all special cases of one general construct of type theory called dependent prod-
uct. This is the mathematical construction corresponding to an indexed family of
functions. A function f ∈ Πx : D ·Cx maps an element x of its domain D to its (in-
dexed) codomain Cx. Thus a proof of ∀x : D · Px is a function mapping an element
x of D to a proof of proposition Px.
30 CHAPTER 1. BASIC PREDICATE CALCULUS
1.4.5 Equality
The basic equality provided in C OQ is Leibniz equality, noted infix like x=y, when
x and y are two expressions of type the same Set. The replacement of x by y in any
term is effected by a variety of tactics, such as rewrite and replace.
Let us give a few examples of equality replacement. Let us assume that some
arithmetic function f is null in zero:
Coq < Variable f : nat -> nat.
f is assumed
Coq < Hypothesis foo : f 0 = 0.
foo is assumed
E : k = 0
============================
f k = k
What happened here is that the replacement left the first subgoal to be proved,
but another proof obligation was generated by the replace tactic, as the second
subgoal. The first subgoal is solved immediately by applying lemma foo; the
second one transitivity and then symmetry of equality, for instance with tactics
transitivity and symmetry:
Coq < apply foo.
1 subgoal
Now, assume that we have loaded a module of general properties about rela-
tions over some abstract type T, such as transitivity:
In order to make any progress, one needs to use the definition of transitive.
The unfold tactic, which replaces all occurrences of a defined notion by its defi-
nition in the current goal, may be used here.
Now, unfolding element would be a mistake, because indeed a simple proof can
be found by auto, keeping element an abstract predicate:
Coq < auto.
No more subgoals.
Many variations on unfold are provided in C OQ. For instance, we may selec-
tively unfold one designated occurrence:
Coq < Undo 2.
1 subgoal
x : set
y : set
z : set
H : subset x y
H0 : forall x : U, element x y -> element x z
============================
subset x z
Coq < unfold subset in H.
1 subgoal
unfold subset at 2.
intros.
unfold subset in H.
red.
auto.
subset_transitive is defined
Induction
2.1.1 Booleans
Let us start with the collection of booleans, as they are specified in the C OQ’s
Prelude module:
Coq < Inductive bool : Set := true | false.
bool is defined
bool_rect is defined
bool_ind is defined
bool_rec is defined
Such a declaration defines several objects at once. First, a new Set is declared,
with name bool. Then the constructors of this Set are declared, called true and
false. Those are analogous to introduction rules of the new Set bool. Finally,
a specific elimination rule for bool is now available, which permits to reason by
cases on bool values. Three instances are indeed defined as new combinators in
the global context: bool_ind, a proof combinator corresponding to reasoning by
cases, bool_rec, an if-then-else programming construct, and bool_rect, a similar
combinator at the level of types. Indeed:
Coq < Check bool_ind.
bool_ind
: forall P : bool -> Prop,
P true -> P false -> forall b : bool, P b
Coq < Check bool_rec.
bool_rec
39
40 CHAPTER 2. INDUCTION
We use the knowledge that b is a bool by calling tactic elim, which is this case
will appeal to combinator bool_ind in order to split the proof according to the two
cases:
Coq < elim b.
2 subgoals
============================
true = true \/ true = false
subgoal 2 is:
false = true \/ false = false
Indeed, the whole proof can be done with the combination of the simple
induction, which combines intro and elim, with good old auto:
For the rest of the session, we shall clean up what we did so far with types bool
and nat, in order to use the initial definitions given in C OQ’s Prelude module, and
not to get confusing error messages due to our redefinitions. We thus revert to the
state before our definition of bool with the Reset command:
Coq < Reset bool.
============================
forall n : nat, n = n + 0
Coq < intro n; elim n.
2 subgoals
n : nat
============================
0 = 0 + 0
subgoal 2 is:
forall n0 : nat, n0 = n0 + 0 -> S n0 = S n0 + 0
44 CHAPTER 2. INDUCTION
What happened was that elim n, in order to construct a Prop (the initial goal)
from a nat (i.e. n), appealed to the corresponding induction principle nat_ind
which we saw was indeed exactly Peano’s induction scheme. Pattern-matching
instantiated the corresponding predicate P to fun n:nat => n = n 0+, and we
get as subgoals the corresponding instantiations of the base case (P O) , and of the
inductive step forall y:nat, P y -> P (S y). In each case we get an instance
of function plus in which its second argument starts with a constructor, and is thus
amenable to simplification by primitive recursion. The C OQ tactic simpl can be
used for this purpose:
n : nat
============================
0 = 0
subgoal 2 is:
forall n0 : nat, n0 = n0 + 0 -> S n0 = S n0 + 0
Coq < auto.
1 subgoal
n : nat
============================
forall n0 : nat, n0 = n0 + 0 -> S n0 = S n0 + 0
Here auto succeeded, because it used as a hint lemma eq_S, which say that
successor preserves equality:
Actually, let us see how to declare our lemma plus_n_O as a hint to be used by
auto:
============================
forall n m : nat, S (n + m) = n + S m
We now go faster, remembering that tactic simple induction does the nec-
essary intros before applying elim. Factoring simplification and automation in
both cases thanks to tactic composition, we prove this lemma in one line:
============================
forall n m : nat, n + m = m + n
n : nat
m : nat
============================
forall n0 : nat,
n + n0 = n0 + n -> n + S n0 = S (n0 + n)
Here auto succeeded on the base case, thanks to our hint plus_n_O, but the
induction step requires rewriting, which auto does not handle:
2.1.4 Discriminate
It is also possible to define new propositions by primitive recursion. Let us for
instance define the predicate which discriminates between the constructors O and
S: it computes to False when its argument is O, and to True when its argument is
of the form (S n):
Now we may use the computational power of Is_S in order to prove trivially
that (Is_S (S n)):
============================
forall n : nat, Is_S (S n)
Coq < simpl; trivial.
No more subgoals.
Coq < Qed.
simpl; trivial.
S_Is_S is defined
But we may also use it to transform a False goal into (Is_S O). Let us show
a particularly important use of this feature; we want to prove that O and S construct
different values, one of Peano’s axioms:
============================
forall n : nat, 0 <> S n
First of all, we replace negation by its definition, by reducing the goal with
tactic red; then we get contradiction by successive intros:
n : nat
H : 0 = S n
============================
False
n : nat
H : 0 = S n
============================
Is_S 0
Now we use equality in order to get a subgoal which computes out to True,
which finishes the proof:
Coq < rewrite H; trivial.
1 subgoal
n : nat
H : 0 = S n
============================
Is_S (S n)
Coq < simpl; trivial.
No more subgoals.
============================
forall n : nat, 0 <> S n
Coq < intro n; discriminate.
No more subgoals.
Coq < Qed.
intro n; discriminate.
no_confusion is defined
get not only the “axioms” le_n and le_S, but also the converse property, that
(le n m) if and only if this statement can be obtained as a consequence of these
defining clauses; that is, le is the minimal predicate verifying clauses le_n and
le_S. This is insured, as in the case of inductive data types, by an elimination prin-
ciple, which here amounts to an induction principle le_ind, stating this minimality
property:
Coq < Check le.
le
: nat -> nat -> Prop
Coq < Check le_ind.
le_ind
: forall (n : nat) (P : nat -> Prop),
P n ->
(forall m : nat, le n m -> P m -> P (S m)) ->
forall n0 : nat, le n n0 -> P n0
Let us show how proofs may be conducted with this principle. First we show
that n ≤ m ⇒ n + 1 ≤ m + 1:
Coq < Lemma le_n_S : forall n m:nat, le n m -> le (S n) (S m).
1 subgoal
============================
forall n m : nat, le n m -> le (S n) (S m)
Coq < intros n m n_le_m.
1 subgoal
n : nat
m : nat
n_le_m : le n m
============================
le (S n) (S m)
Coq < elim n_le_m.
2 subgoals
n : nat
m : nat
n_le_m : le n m
============================
le (S n) (S n)
subgoal 2 is:
forall m0 : nat,
le n m0 -> le (S n) (S m0) -> le (S n) (S (S m0))
n : nat
m : nat
n_le_m : le n m
============================
forall m0 : nat,
le n m0 -> le (S n) (S m0) -> le (S n) (S (S m0))
Coq < intros; apply le_S; trivial.
No more subgoals.
Now we know that it is a good idea to give the defining clauses as hints, so that
the proof may proceed with a simple combination of induction and auto.
Coq < Restart.
1 subgoal
============================
forall n m : nat, le n m -> le (S n) (S m)
Coq < Hint Resolve le_n le_S .
============================
forall n : nat, le n 0 -> n = 0
However, here trying something like induction 1 would lead nowhere (try it
and see what happens). An induction on n would not be convenient either. What we
must do here is analyse the definition of le in order to match hypothesis (le n O)
with the defining clauses, to find that only le_n applies, whence the result. This
analysis may be performed by the “inversion” tactic inversion_clear as follows:
Coq < intros n H; inversion_clear H.
1 subgoal
50 CHAPTER 2. INDUCTION
n : nat
============================
0 = 0
Coq < trivial.
No more subgoals.
Coq < Qed.
intros n H; inversion_clear H.
trivial.
tricky is defined
Chapter 3
Modules
Such a command looks for a (compiled) module file Arith.vo in the libraries
registered by C OQ. Libraries inherit the structure of the file system of the operating
system and are registered with the command Add LoadPath. Physical directories
are mapped to logical directories. Especially the standard library of C OQ is pre-
registered as a library of name Coq. Modules have absolute unique names denoting
their place in C OQ libraries. An absolute name is a sequence of single identifiers
separated by dots. E.g. the module Arith has full name Coq.Arith.Arith and
because it resides in eponym subdirectory Arith of the standard library, it can be
as well required by the command
51
52 CHAPTER 3. MODULES
Another command Search displays only lemmas where the searched predicate
appears at the head position in the conclusion.
Coq < Search le.
le_S: forall n m : nat, le n m -> le n (S m)
le_n: forall n : nat, le n n
Top.le_n_S: forall n m : nat, le n m -> le (S n) (S m)