Built with Alectryon, running Coq+SerAPI v8.10.0+0.7.0. Coq sources are in this panel; goals and messages will appear in the other. Bubbles () indicate interactive fragments: hover for details, tap to reveal contents. Use Ctrl+↑ Ctrl+↓ to navigate, Ctrl+🖱️ to focus.
Set Warnings "-notation-overridden,-parsing". From LF Require Export Tactics.
In previous chapters, we have seen many examples of factual
claims (propositions) and ways of presenting evidence of their
truth (proofs). In particular, we have worked extensively with
equality propositions of the form e1 = e2, with
implications (P → Q), and with quantified propositions (∀
x, P). In this chapter, we will see how Coq can be used to carry
out other familiar forms of logical reasoning.
Before diving into details, let's talk a bit about the status of
mathematical statements in Coq. Recall that Coq is a typed
language, which means that every sensible expression in its world
has an associated type. Logical claims are no exception: any
statement we might try to prove in Coq has a type, namely Prop,
the type of propositions. We can see this with the Check
command:
Note that all syntactically well-formed propositions have type
Prop in Coq, regardless of whether they are true.
Simply being a proposition is one thing; being provable is
something else!
Indeed, propositions don't just have types: they are
first-class objects that can be manipulated in the same ways as
the other entities in Coq's world.
So far, we've seen one primary place that propositions can appear:
in Theorem (and Lemma and Example) declarations.
2 + 2 = 4reflexivity. Qed.2 + 2 = 4
But propositions can be used in many other ways. For example, we
can give a name to a proposition using a Definition, just as we
have given names to expressions of other sorts.
Definition plus_fact : Prop := 2 + 2 = 4.
We can later use this name in any situation where a proposition is
expected -- for example, as the claim in a Theorem declaration.
plus_factreflexivity. Qed.plus_fact
We can also write parameterized propositions -- that is,
functions that take arguments of some type and return a
proposition.
For instance, the following function takes a number
and returns a proposition asserting that this number is equal to
three:
Definition is_three (n : nat) : Prop := n = 3.
In Coq, functions that return propositions are said to define
properties of their arguments.
For instance, here's a (polymorphic) property defining the
familiar notion of an injective function.
Definition injective {A B} (f : A -> B) := forall x y : A, f x = f y -> x = y.injective Sinjective Sn, m:natH:S n = S mn = mapply H1. Qed.n, m:natH1:n = mn = m
The equality operator = is also a function that returns a
Prop.
The expression n = m is syntactic sugar for eq n m (defined
using Coq's Notation mechanism). Because eq can be used with
elements of any type, it is also polymorphic:
(Notice that we wrote @eq instead of eq: The type
argument A to eq is declared as implicit, so we need to turn
off implicit arguments to see the full type of eq.)
(* ################################################################# *)
(* ================================================================= *)
The conjunction, or logical and, of propositions A and B
is written A ∧ B, representing the claim that both A and B
are true.
3 + 4 = 7 /\ 2 * 2 = 4
To prove a conjunction, use the split tactic. It will generate
two subgoals, one for each part of the statement:
3 + 4 = 7 /\ 2 * 2 = 43 + 4 = 72 * 2 = 4reflexivity.3 + 4 = 7reflexivity. Qed.2 * 2 = 4
For any propositions A and B, if we assume that A is true
and we assume that B is true, we can conclude that A ∧ B is
also true.
forall A B : Prop, A -> B -> A /\ Bforall A B : Prop, A -> B -> A /\ BA, B:PropHA:AHB:BA /\ BA, B:PropHA:AHB:BAA, B:PropHA:AHB:BBapply HA.A, B:PropHA:AHB:BAapply HB. Qed.A, B:PropHA:AHB:BB
Since applying a theorem with hypotheses to some goal has the
effect of generating as many subgoals as there are hypotheses for
that theorem, we can apply and_intro to achieve the same effect
as split.
3 + 4 = 7 /\ 2 * 2 = 43 + 4 = 7 /\ 2 * 2 = 43 + 4 = 72 * 2 = 4reflexivity.3 + 4 = 7reflexivity. Qed.2 * 2 = 4
forall n m : nat, n + m = 0 -> n = 0 /\ m = 0(* FILL IN HERE *) Admitted.forall n m : nat, n + m = 0 -> n = 0 /\ m = 0
☐
So much for proving conjunctive statements. To go in the other
direction -- i.e., to use a conjunctive hypothesis to help prove
something else -- we employ the destruct tactic.
If the proof context contains a hypothesis H of the form
A ∧ B, writing destruct H as [HA HB] will remove H from the
context and add two new hypotheses: HA, stating that A is
true, and HB, stating that B is true.
forall n m : nat, n = 0 /\ m = 0 -> n + m = 0(* WORKED IN CLASS *)forall n m : nat, n = 0 /\ m = 0 -> n + m = 0n, m:natH:n = 0 /\ m = 0n + m = 0n, m:natHn:n = 0Hm:m = 0n + m = 0n, m:natHn:n = 0Hm:m = 00 + m = 0reflexivity. Qed.n, m:natHn:n = 0Hm:m = 00 + 0 = 0
As usual, we can also destruct H right when we introduce it,
instead of introducing and then destructing it:
forall n m : nat, n = 0 /\ m = 0 -> n + m = 0forall n m : nat, n = 0 /\ m = 0 -> n + m = 0n, m:natHn:n = 0Hm:m = 0n + m = 0n, m:natHn:n = 0Hm:m = 00 + m = 0reflexivity. Qed.n, m:natHn:n = 0Hm:m = 00 + 0 = 0
You may wonder why we bothered packing the two hypotheses n = 0
and m = 0 into a single conjunction, since we could have also
stated the theorem with two separate premises:
forall n m : nat, n = 0 -> m = 0 -> n + m = 0forall n m : nat, n = 0 -> m = 0 -> n + m = 0n, m:natHn:n = 0Hm:m = 0n + m = 0n, m:natHn:n = 0Hm:m = 00 + m = 0reflexivity. Qed.n, m:natHn:n = 0Hm:m = 00 + 0 = 0
For this theorem, both formulations are fine. But it's important
to understand how to work with conjunctive hypotheses because
conjunctions often arise from intermediate steps in proofs,
especially in bigger developments. Here's a simple example:
forall n m : nat, n + m = 0 -> n * m = 0(* WORKED IN CLASS *)forall n m : nat, n + m = 0 -> n * m = 0n, m:natH:n + m = 0n * m = 0n, m:natH:n + m = 0n = 0 /\ m = 0n, m:natH:n + m = 0H':n = 0 /\ m = 0n * m = 0n, m:natH:n + m = 0n = 0 /\ m = 0apply H.n, m:natH:n + m = 0n + m = 0n, m:natH:n + m = 0H':n = 0 /\ m = 0n * m = 0n, m:natH:n + m = 0Hn:n = 0Hm:m = 0n * m = 0reflexivity. Qed.n, m:natH:n + m = 0Hn:n = 0Hm:m = 00 * m = 0
Another common situation with conjunctions is that we know
A ∧ B but in some context we need just A (or just B).
The following lemmas are useful in such cases:
forall P Q : Prop, P /\ Q -> Pforall P Q : Prop, P /\ Q -> Papply HP. Qed.P, Q:PropHP:PHQ:QP
forall P Q : Prop, P /\ Q -> Q(* FILL IN HERE *) Admitted.forall P Q : Prop, P /\ Q -> Q
☐
Finally, we sometimes need to rearrange the order of conjunctions
and/or the grouping of multi-way conjunctions. The following
commutativity and associativity theorems are handy in such
cases.
forall P Q : Prop, P /\ Q -> Q /\ Pforall P Q : Prop, P /\ Q -> Q /\ PP, Q:PropHP:PHQ:QQ /\ PP, Q:PropHP:PHQ:QQP, Q:PropHP:PHQ:QPapply HQ.P, Q:PropHP:PHQ:QQapply HP. Qed.P, Q:PropHP:PHQ:QP
Exercise: 2 stars, standard (and_assoc)
forall P Q R : Prop, P /\ Q /\ R -> (P /\ Q) /\ Rforall P Q R : Prop, P /\ Q /\ R -> (P /\ Q) /\ R(* FILL IN HERE *) Admitted.P, Q, R:PropHP:PHQ:QHR:R(P /\ Q) /\ R
☐
By the way, the infix notation ∧ is actually just syntactic
sugar for and A B. That is, and is a Coq operator that takes
two propositions as arguments and yields a proposition.
(* ================================================================= *)
Another important connective is the disjunction, or logical or,
of two propositions: A ∨ B is true when either A or B
is. (This infix notation stands for or A B, where or : Prop →
Prop → Prop.)
To use a disjunctive hypothesis in a proof, we proceed by case
analysis, which, as for nat or other data types, can be done
explicitly with destruct or implicitly with an intros pattern:
forall n m : nat, n = 0 \/ m = 0 -> n * m = 0(* This pattern implicitly does case analysis on [n = 0 \/ m = 0] *)forall n m : nat, n = 0 \/ m = 0 -> n * m = 0n, m:natHn:n = 0n * m = 0n, m:natHm:m = 0n * m = 0n, m:natHn:n = 0n * m = 0reflexivity.n, m:natHn:n = 00 * m = 0n, m:natHm:m = 0n * m = 0n, m:natHm:m = 0n * 0 = 0reflexivity. Qed.n, m:natHm:m = 00 = 0
Conversely, to show that a disjunction holds, we need to show that
one of its sides does. This is done via two tactics, left and
right. As their names imply, the first one requires
proving the left side of the disjunction, while the second
requires proving its right side. Here is a trivial use...
forall A B : Prop, A -> A \/ Bforall A B : Prop, A -> A \/ BA, B:PropHA:AA \/ Bapply HA. Qed.A, B:PropHA:AA
... and here is a slightly more interesting example requiring both
left and right:
forall n : nat, n = 0 \/ n = S (Nat.pred n)(* WORKED IN CLASS *)forall n : nat, n = 0 \/ n = S (Nat.pred n)0 = 0 \/ 0 = S (Nat.pred 0)n:natS n = 0 \/ S n = S (Nat.pred (S n))0 = 0 \/ 0 = S (Nat.pred 0)reflexivity.0 = 0n:natS n = 0 \/ S n = S (Nat.pred (S n))reflexivity. Qed.n:natS n = S (Nat.pred (S n))
forall n m : nat, n * m = 0 -> n = 0 \/ m = 0(* FILL IN HERE *) Admitted.forall n m : nat, n * m = 0 -> n = 0 \/ m = 0
☐
forall P Q : Prop, P \/ Q -> Q \/ P(* FILL IN HERE *) Admitted.forall P Q : Prop, P \/ Q -> Q \/ P
☐
(* ================================================================= *)
Falsehood and Negation
To see how negation works, recall the principle of explosion
from the Tactics chapter; it asserts that, if we assume a
contradiction, then any other proposition can be derived.
Following this intuition, we could define ¬ P ("not P") as
∀ Q, P → Q.
Coq actually makes a slightly different (but equivalent) choice,
defining ¬ P as P → False, where False is a specific
contradictory proposition defined in the standard library.
Module MyNot. Definition not (P:Prop) := P -> False. Notation "~ x" := (not x) : type_scope.End MyNot.
Since False is a contradictory proposition, the principle of
explosion also applies to it. If we get False into the proof
context, we can use destruct on it to complete any goal:
forall P : Prop, False -> P(* WORKED IN CLASS *)forall P : Prop, False -> Pdestruct contra. Qed.P:Propcontra:FalseP
The Latin ex falso quodlibet means, literally, "from falsehood
follows whatever you like"; this is another common name for the
principle of explosion.
Exercise: 2 stars, standard, optional (not_implies_our_not)
forall P : Prop, ~ P -> forall Q : Prop, P -> Q(* FILL IN HERE *) Admitted.forall P : Prop, ~ P -> forall Q : Prop, P -> Q
☐
Inequality is a frequent enough example of negated statement
that there is a special notation for it, x ≠ y:
Notation "x <> y" := (~(x = y)).
We can use not to state that 0 and 1 are different elements
of nat:
0 <> 10 <> 1
The proposition 0 ≠ 1 is exactly the same as
~(0 = 1), that is not (0 = 1), which unfolds to
(0 = 1) → False. (We use unfold not explicitly here
to illustrate that point, but generally it can be omitted.)
0 = 1 -> False
To prove an inequality, we may assume the opposite
equality...
contra:0 = 1False
... and deduce a contradiction from it. Here, the
equality O = S O contradicts the disjointness of
constructors O and S, so discriminate takes care
of it.
discriminate contra. Qed.
It takes a little practice to get used to working with negation in
Coq. Even though you can see perfectly well why a statement
involving negation is true, it can be a little tricky at first to
get things into the right configuration so that Coq can understand
it! Here are proofs of a few familiar facts to get you warmed
up.
~ False~ FalseFalse -> Falsedestruct H. Qed.H:FalseFalseforall P Q : Prop, P /\ ~ P -> Q(* WORKED IN CLASS *)forall P Q : Prop, P /\ ~ P -> QP, Q:PropHP:PHNA:~ PQP, Q:PropHP:PHNA:P -> FalseQdestruct HP. Qed.P, Q:PropHP:FalseHNA:P -> FalseQforall P : Prop, P -> ~ ~ P(* WORKED IN CLASS *)forall P : Prop, P -> ~ ~ PP:PropH:P~ ~ PP:PropH:P(P -> False) -> FalseP:PropH:PG:P -> FalseFalseapply H. Qed.P:PropH:PG:P -> FalseP
Exercise: 2 stars, advanced (double_neg_inf)
(* FILL IN HERE *) (* Do not modify the following line: *) Definition manual_grade_for_double_neg_inf : option (nat*string) := None.
☐
forall P Q : Prop, (P -> Q) -> ~ Q -> ~ P(* FILL IN HERE *) Admitted.forall P Q : Prop, (P -> Q) -> ~ Q -> ~ P
☐
forall P : Prop, ~ (P /\ ~ P)(* FILL IN HERE *) Admitted.forall P : Prop, ~ (P /\ ~ P)
☐
Exercise: 1 star, advanced (informal_not_PNP)
(* FILL IN HERE *) (* Do not modify the following line: *) Definition manual_grade_for_informal_not_PNP : option (nat*string) := None.
☐
Similarly, since inequality involves a negation, it requires a
little practice to be able to work with it fluently. Here is one
useful trick. If you are trying to prove a goal that is
nonsensical (e.g., the goal state is false = true), apply
ex_falso_quodlibet to change the goal to False. This makes it
easier to use assumptions of the form ¬P that may be available
in the context -- in particular, assumptions of the form
x≠y.
forall b : bool, b <> true -> b = falseforall b : bool, b <> true -> b = falseH:true <> truetrue = falseH:false <> truefalse = falseH:true <> truetrue = falseH:true = true -> Falsetrue = falseH:true = true -> FalseFalsereflexivity.H:true = true -> Falsetrue = truereflexivity. Qed.H:false <> truefalse = false
Since reasoning with ex_falso_quodlibet is quite common, Coq
provides a built-in tactic, exfalso, for applying it.
forall b : bool, b <> true -> b = falseforall b : bool, b <> true -> b = falseH:true <> truetrue = falseH:false <> truefalse = falseH:true <> truetrue = falseH:true = true -> Falsetrue = falseH:true = true -> FalseFalsereflexivity.H:true = true -> Falsetrue = truereflexivity. Qed. (* ================================================================= *)H:false <> truefalse = false
Besides False, Coq's standard library also defines True, a
proposition that is trivially true. To prove it, we use the
predefined constant I : True:
Trueapply I. Qed.True
Unlike False, which is used extensively, True is used quite
rarely, since it is trivial (and therefore uninteresting) to prove
as a goal, and it carries no useful information as a hypothesis.
But it can be quite useful when defining complex Props using
conditionals or as a parameter to higher-order Props.
We will see examples of such uses of True later on.
(* ================================================================= *)
The handy "if and only if" connective, which asserts that two
propositions have the same truth value, is just the conjunction of
two implications.
Module MyIff. Definition iff (P Q : Prop) := (P -> Q) /\ (Q -> P). Notation "P <-> Q" := (iff P Q) (at level 95, no associativity) : type_scope. End MyIff.forall P Q : Prop, P <-> Q -> Q <-> P(* WORKED IN CLASS *)forall P Q : Prop, P <-> Q -> Q <-> PP, Q:PropHAB:P -> QHBA:Q -> PQ <-> PP, Q:PropHAB:P -> QHBA:Q -> PQ -> PP, Q:PropHAB:P -> QHBA:Q -> PP -> Qapply HBA.P, Q:PropHAB:P -> QHBA:Q -> PQ -> Papply HAB. Qed.P, Q:PropHAB:P -> QHBA:Q -> PP -> Qforall b : bool, b <> true <-> b = false(* WORKED IN CLASS *)forall b : bool, b <> true <-> b = falseb:boolb <> true <-> b = falseb:boolb <> true -> b = falseb:boolb = false -> b <> trueapply not_true_is_false.b:boolb <> true -> b = falseb:boolb = false -> b <> trueb:boolH:b = falseb <> trueb:boolH:b = falsefalse <> truediscriminate H'. Qed.b:boolH:b = falseH':false = trueFalse
Exercise: 1 star, standard, optional (iff_properties)
forall P : Prop, P <-> P(* FILL IN HERE *) Admitted.forall P : Prop, P <-> Pforall P Q R : Prop, P <-> Q -> Q <-> R -> P <-> R(* FILL IN HERE *) Admitted.forall P Q R : Prop, P <-> Q -> Q <-> R -> P <-> R
☐
forall P Q R : Prop, P \/ Q /\ R <-> (P \/ Q) /\ (P \/ R)(* FILL IN HERE *) Admitted.forall P Q R : Prop, P \/ Q /\ R <-> (P \/ Q) /\ (P \/ R)
☐
Some of Coq's tactics treat iff statements specially, avoiding
the need for some low-level proof-state manipulation. In
particular, rewrite and reflexivity can be used with iff
statements, not just equalities. To enable this behavior, we need
to import a Coq library that supports it:
From Coq Require Import Setoids.Setoid.
Here is a simple example demonstrating how these tactics work with
iff. First, let's prove a couple of basic iff equivalences...
forall n m : nat, n * m = 0 <-> n = 0 \/ m = 0forall n m : nat, n * m = 0 <-> n = 0 \/ m = 0n, m:natn * m = 0 -> n = 0 \/ m = 0n, m:natn = 0 \/ m = 0 -> n * m = 0apply mult_eq_0.n, m:natn * m = 0 -> n = 0 \/ m = 0apply or_example. Qed.n, m:natn = 0 \/ m = 0 -> n * m = 0forall P Q R : Prop, P \/ Q \/ R <-> (P \/ Q) \/ Rforall P Q R : Prop, P \/ Q \/ R <-> (P \/ Q) \/ RP, Q, R:PropP \/ Q \/ R <-> (P \/ Q) \/ RP, Q, R:PropP \/ Q \/ R -> (P \/ Q) \/ RP, Q, R:Prop(P \/ Q) \/ R -> P \/ Q \/ RP, Q, R:PropP \/ Q \/ R -> (P \/ Q) \/ RP, Q, R:PropH:P(P \/ Q) \/ RP, Q, R:PropH:Q(P \/ Q) \/ RP, Q, R:PropH:R(P \/ Q) \/ RP, Q, R:PropH:P(P \/ Q) \/ RP, Q, R:PropH:PP \/ Qapply H.P, Q, R:PropH:PPP, Q, R:PropH:Q(P \/ Q) \/ RP, Q, R:PropH:QP \/ Qapply H.P, Q, R:PropH:QQP, Q, R:PropH:R(P \/ Q) \/ Rapply H.P, Q, R:PropH:RRP, Q, R:Prop(P \/ Q) \/ R -> P \/ Q \/ RP, Q, R:PropH:PP \/ Q \/ RP, Q, R:PropH:QP \/ Q \/ RP, Q, R:PropH:RP \/ Q \/ RP, Q, R:PropH:PP \/ Q \/ Rapply H.P, Q, R:PropH:PPP, Q, R:PropH:QP \/ Q \/ RP, Q, R:PropH:QQ \/ Rapply H.P, Q, R:PropH:QQP, Q, R:PropH:RP \/ Q \/ RP, Q, R:PropH:RQ \/ Rapply H. Qed.P, Q, R:PropH:RR
We can now use these facts with rewrite and reflexivity to
give smooth proofs of statements involving equivalences. Here is
a ternary version of the previous mult_0 result:
forall n m p : nat, n * m * p = 0 <-> n = 0 \/ m = 0 \/ p = 0forall n m p : nat, n * m * p = 0 <-> n = 0 \/ m = 0 \/ p = 0n, m, p:natn * m * p = 0 <-> n = 0 \/ m = 0 \/ p = 0n, m, p:natn * m = 0 \/ p = 0 <-> n = 0 \/ m = 0 \/ p = 0n, m, p:nat(n = 0 \/ m = 0) \/ p = 0 <-> n = 0 \/ m = 0 \/ p = 0reflexivity. Qed.n, m, p:nat(n = 0 \/ m = 0) \/ p = 0 <-> (n = 0 \/ m = 0) \/ p = 0
The apply tactic can also be used with ↔. When given an
equivalence as its argument, apply tries to guess which side of
the equivalence to use.
forall n m : nat, n * m = 0 -> n = 0 \/ m = 0forall n m : nat, n * m = 0 -> n = 0 \/ m = 0n, m:natH:n * m = 0n = 0 \/ m = 0apply H. Qed. (* ================================================================= *)n, m:natH:n * m = 0n * m = 0
Another important logical connective is existential
quantification. To say that there is some x of type T such
that some property P holds of x, we write ∃ x : T,
P. As with ∀, the type annotation : T can be omitted if
Coq is able to infer from the context what the type of x should
be.
To prove a statement of the form ∃ x, P, we must show that
P holds for some specific choice of value for x, known as the
witness of the existential. This is done in two steps: First,
we explicitly tell Coq which witness t we have in mind by
invoking the tactic ∃ t. Then we prove that P holds after
all occurrences of x are replaced by t.
exists n : nat, 4 = n + nexists n : nat, 4 = n + nreflexivity. Qed.4 = 2 + 2
Conversely, if we have an existential hypothesis ∃ x, P in
the context, we can destruct it to obtain a witness x and a
hypothesis stating that P holds of x.
forall n : nat, (exists m : nat, n = 4 + m) -> exists o : nat, n = 2 + o(* WORKED IN CLASS *)forall n : nat, (exists m : nat, n = 4 + m) -> exists o : nat, n = 2 + on, m:natHm:n = 4 + mexists o : nat, n = 2 + oapply Hm. Qed.n, m:natHm:n = 4 + mn = 2 + (2 + m)
Exercise: 1 star, standard, recommended (dist_not_exists)
forall (X : Type) (P : X -> Prop), (forall x : X, P x) -> ~ (exists x : X, ~ P x)(* FILL IN HERE *) Admitted.forall (X : Type) (P : X -> Prop), (forall x : X, P x) -> ~ (exists x : X, ~ P x)
☐
Exercise: 2 stars, standard (dist_exists_or)
forall (X : Type) (P Q : X -> Prop), (exists x : X, P x \/ Q x) <-> (exists x : X, P x) \/ (exists x : X, Q x)(* FILL IN HERE *) Admitted.forall (X : Type) (P Q : X -> Prop), (exists x : X, P x \/ Q x) <-> (exists x : X, P x) \/ (exists x : X, Q x)
☐
(* ################################################################# *)
The logical connectives that we have seen provide a rich
vocabulary for defining complex propositions from simpler ones.
To illustrate, let's look at how to express the claim that an
element x occurs in a list l. Notice that this property has a
simple recursive structure:
- If l is the empty list, then x cannot occur on it, so the
property "x appears in l" is simply false.
- Otherwise, l has the form x' :: l'. In this case, x occurs in l if either it is equal to x' or it occurs in l'.
We can translate this directly into a straightforward recursive
function taking an element and a list and returning a proposition:
Fixpoint In {A : Type} (x : A) (l : list A) : Prop :=
match l with
| [] => False
| x' :: l' => x' = x \/ In x l'
end.
When In is applied to a concrete list, it expands into a
concrete sequence of nested disjunctions.
In 4 [1; 2; 3; 4; 5](* WORKED IN CLASS *)In 4 [1; 2; 3; 4; 5]1 = 4 \/ 2 = 4 \/ 3 = 4 \/ 4 = 4 \/ 5 = 4 \/ False2 = 4 \/ 3 = 4 \/ 4 = 4 \/ 5 = 4 \/ False3 = 4 \/ 4 = 4 \/ 5 = 4 \/ False4 = 4 \/ 5 = 4 \/ Falsereflexivity. Qed.4 = 4forall n : nat, In n [2; 4] -> exists n' : nat, n = 2 * n'(* WORKED IN CLASS *)forall n : nat, In n [2; 4] -> exists n' : nat, n = 2 * n'forall n : nat, 2 = n \/ 4 = n \/ False -> exists n' : nat, n = n' + (n' + 0)n:natH:2 = nexists n' : nat, n = n' + (n' + 0)n:natH:4 = nexists n' : nat, n = n' + (n' + 0)n:natH:2 = nexists n' : nat, n = n' + (n' + 0)n:natH:2 = nn = 1 + (1 + 0)reflexivity.n:natH:2 = n2 = 1 + (1 + 0)n:natH:4 = nexists n' : nat, n = n' + (n' + 0)n:natH:4 = nn = 2 + (2 + 0)reflexivity. Qed.n:natH:4 = n4 = 2 + (2 + 0)
(Notice the use of the empty pattern to discharge the last case
en passant.)
We can also prove more generic, higher-level lemmas about In.
Note, in the next, how In starts out applied to a variable and
only gets expanded when we do case analysis on this variable:
forall (A B : Type) (f : A -> B) (l : list A) (x : A), In x l -> In (f x) (map f l)forall (A B : Type) (f : A -> B) (l : list A) (x : A), In x l -> In (f x) (map f l)A, B:Typef:A -> Bl:list Ax:AIn x l -> In (f x) (map f l)A, B:Typef:A -> Bx:AIn x [ ] -> In (f x) (map f [ ])A, B:Typef:A -> Bx':Al':list Ax:AIHl':In x l' -> In (f x) (map f l')In x (x' :: l') -> In (f x) (map f (x' :: l'))A, B:Typef:A -> Bx:AIn x [ ] -> In (f x) (map f [ ])intros [].A, B:Typef:A -> Bx:AFalse -> FalseA, B:Typef:A -> Bx':Al':list Ax:AIHl':In x l' -> In (f x) (map f l')In x (x' :: l') -> In (f x) (map f (x' :: l'))A, B:Typef:A -> Bx':Al':list Ax:AIHl':In x l' -> In (f x) (map f l')x' = x \/ In x l' -> f x' = f x \/ In (f x) (map f l')A, B:Typef:A -> Bx':Al':list Ax:AIHl':In x l' -> In (f x) (map f l')H:x' = xf x' = f x \/ In (f x) (map f l')A, B:Typef:A -> Bx':Al':list Ax:AIHl':In x l' -> In (f x) (map f l')H:In x l'f x' = f x \/ In (f x) (map f l')A, B:Typef:A -> Bx':Al':list Ax:AIHl':In x l' -> In (f x) (map f l')H:x' = xf x' = f x \/ In (f x) (map f l')A, B:Typef:A -> Bx':Al':list Ax:AIHl':In x l' -> In (f x) (map f l')H:x' = xf x = f x \/ In (f x) (map f l')reflexivity.A, B:Typef:A -> Bx':Al':list Ax:AIHl':In x l' -> In (f x) (map f l')H:x' = xf x = f xA, B:Typef:A -> Bx':Al':list Ax:AIHl':In x l' -> In (f x) (map f l')H:In x l'f x' = f x \/ In (f x) (map f l')A, B:Typef:A -> Bx':Al':list Ax:AIHl':In x l' -> In (f x) (map f l')H:In x l'In (f x) (map f l')apply H. Qed.A, B:Typef:A -> Bx':Al':list Ax:AIHl':In x l' -> In (f x) (map f l')H:In x l'In x l'
This way of defining propositions recursively, though convenient
in some cases, also has some drawbacks. In particular, it is
subject to Coq's usual restrictions regarding the definition of
recursive functions, e.g., the requirement that they be "obviously
terminating." In the next chapter, we will see how to define
propositions inductively, a different technique with its own set
of strengths and limitations.
forall (A B : Type) (f : A -> B) (l : list A) (y : B), In y (map f l) <-> (exists x : A, f x = y /\ In x l)(* FILL IN HERE *) Admitted.forall (A B : Type) (f : A -> B) (l : list A) (y : B), In y (map f l) <-> (exists x : A, f x = y /\ In x l)
☐
forall (A : Type) (l l' : list A) (a : A), In a (l ++ l') <-> In a l \/ In a l'(* FILL IN HERE *) Admitted.forall (A : Type) (l l' : list A) (a : A), In a (l ++ l') <-> In a l \/ In a l'
☐
Exercise: 3 stars, standard, recommended (All)
Admitted.All:forall T0 : Type, (T0 -> Prop) -> list T0 -> PropT:TypeP:T -> Propl:list TPropforall (T : Type) (P : T -> Prop) (l : list T), (forall x : T, In x l -> P x) <-> All P l(* FILL IN HERE *) Admitted.forall (T : Type) (P : T -> Prop) (l : list T), (forall x : T, In x l -> P x) <-> All P l
☐
Exercise: 3 stars, standard (combine_odd_even)
Admitted.Podd, Peven:nat -> Propnat -> Prop
To test your definition, prove the following facts:
forall (Podd Peven : nat -> Prop) (n : nat), (oddb n = true -> Podd n) -> (oddb n = false -> Peven n) -> combine_odd_even Podd Peven n(* FILL IN HERE *) Admitted.forall (Podd Peven : nat -> Prop) (n : nat), (oddb n = true -> Podd n) -> (oddb n = false -> Peven n) -> combine_odd_even Podd Peven nforall (Podd Peven : nat -> Prop) (n : nat), combine_odd_even Podd Peven n -> oddb n = true -> Podd n(* FILL IN HERE *) Admitted.forall (Podd Peven : nat -> Prop) (n : nat), combine_odd_even Podd Peven n -> oddb n = true -> Podd nforall (Podd Peven : nat -> Prop) (n : nat), combine_odd_even Podd Peven n -> oddb n = false -> Peven n(* FILL IN HERE *) Admitted.forall (Podd Peven : nat -> Prop) (n : nat), combine_odd_even Podd Peven n -> oddb n = false -> Peven n
☐
(* ################################################################# *)
One feature of Coq that distinguishes it from some other
popular proof assistants (e.g., ACL2 and Isabelle) is that it
treats proofs as first-class objects.
There is a great deal to be said about this, but it is not
necessary to understand it all in detail in order to use Coq. This
section gives just a taste, while a deeper exploration can be
found in the optional chapters ProofObjects and
IndPrinciples.
We have seen that we can use the Check command to ask Coq to
print the type of an expression. We can also use Check to ask
what theorem a particular identifier refers to.
Coq prints the statement of the plus_comm theorem in the same
way that it prints the type of any term that we ask it to
Check. Why?
The reason is that the identifier plus_comm actually refers to a
proof object -- a data structure that represents a logical
derivation establishing of the truth of the statement ∀ n m
: nat, n + m = m + n. The type of this object is the statement
of the theorem that it is a proof of.
Intuitively, this makes sense because the statement of a theorem
tells us what we can use that theorem for, just as the type of a
computational object tells us what we can do with that object --
e.g., if we have a term of type nat → nat → nat, we can give
it two nats as arguments and get a nat back. Similarly, if we
have an object of type n = m → n + n = m + m and we provide it
an "argument" of type n = m, we can derive n + n = m + m.
Operationally, this analogy goes even further: by applying a
theorem, as if it were a function, to hypotheses with matching
types, we can specialize its result without having to resort to
intermediate assertions. For example, suppose we wanted to prove
the following result:
forall x y z : nat, x + (y + z) = z + y + x
It appears at first sight that we ought to be able to prove this
by rewriting with plus_comm twice to make the two sides match.
The problem, however, is that the second rewrite will undo the
effect of the first.
(* WORKED IN CLASS *)forall x y z : nat, x + (y + z) = z + y + xx, y, z:natx + (y + z) = z + y + xx, y, z:naty + z + x = z + y + x(* We are back where we started... *) Abort.x, y, z:natx + (y + z) = z + y + x
One simple way of fixing this problem, using only tools that we
already know, is to use assert to derive a specialized version
of plus_comm that can be used to rewrite exactly where we
want.
forall x y z : nat, x + (y + z) = z + y + xforall x y z : nat, x + (y + z) = z + y + xx, y, z:natx + (y + z) = z + y + xx, y, z:naty + z + x = z + y + xx, y, z:naty + z = z + yx, y, z:natH:y + z = z + yy + z + x = z + y + xx, y, z:naty + z = z + yreflexivity.x, y, z:natz + y = z + yx, y, z:natH:y + z = z + yy + z + x = z + y + xreflexivity. Qed.x, y, z:natH:y + z = z + yz + y + x = z + y + x
A more elegant alternative is to apply plus_comm directly to the
arguments we want to instantiate it with, in much the same way as
we apply a polymorphic function to a type argument.
forall x y z : nat, x + (y + z) = z + y + xforall x y z : nat, x + (y + z) = z + y + xx, y, z:natx + (y + z) = z + y + xx, y, z:naty + z + x = z + y + xreflexivity. Qed.x, y, z:natz + y + x = z + y + x
Let us show another example of using a theorem or lemma
like a function. The following theorem says: any list l
containing some element must be nonempty.
forall (A : Type) (x : A) (l : list A), In x l -> l <> [ ]forall (A : Type) (x : A) (l : list A), In x l -> l <> [ ]A:Typex:Al:list AH:In x ll <> [ ]A:Typex:Al:list AH:In x ll = [ ] -> FalseA:Typex:Al:list AH:In x lHl:l = [ ]FalseA:Typex:AH:In x [ ]Hl:[ ] = [ ]FalseA:Typex, x0:Al:list AH:In x (x0 :: l)Hl:x0 :: l = [ ]FalseA:Typex:AH:In x [ ]Hl:[ ] = [ ]Falsedestruct H.A:Typex:AH:FalseHl:[ ] = [ ]Falsediscriminate Hl. Qed.A:Typex, x0:Al:list AH:In x (x0 :: l)Hl:x0 :: l = [ ]False
What makes this interesting is that one quantified variable
(x) does not appear in the conclusion (l ≠ []).
We can use this lemma to prove the special case where x
is 42. Naively, the tactic apply in_not_nil will fail because
it cannot infer the value of x. There are several ways to work
around that...
forall l : list nat, In 42 l -> l <> [ ](* WORKED IN CLASS *)forall l : list nat, In 42 l -> l <> [ ]l:list natH:In 42 ll <> [ ]Abort. (* [apply ... with ...] *)forall l : list nat, In 42 l -> l <> [ ]forall l : list nat, In 42 l -> l <> [ ]l:list natH:In 42 ll <> [ ]apply H. Qed. (* [apply ... in ...] *)l:list natH:In 42 lIn 42 lforall l : list nat, In 42 l -> l <> [ ]forall l : list nat, In 42 l -> l <> [ ]l:list natH:In 42 ll <> [ ]apply H. Qed. (* Explicitly apply the lemma to the value for [x]. *)l:list natH:l <> [ ]l <> [ ]forall l : list nat, In 42 l -> l <> [ ]forall l : list nat, In 42 l -> l <> [ ]l:list natH:In 42 ll <> [ ]apply H. Qed. (* Explicitly apply the lemma to a hypothesis. *)l:list natH:In 42 lIn 42 lforall l : list nat, In 42 l -> l <> [ ]forall l : list nat, In 42 l -> l <> [ ]apply (in_not_nil _ _ _ H). Qed.l:list natH:In 42 ll <> [ ]
You can "use theorems as functions" in this way with almost all
tactics that take a theorem name as an argument. Note also that
theorem application uses the same inference mechanisms as function
application; thus, it is possible, for example, to supply
wildcards as arguments to be inferred, or to declare some
hypotheses to a theorem as implicit by default. These features
are illustrated in the proof below. (The details of how this proof
works are not critical -- the goal here is just to illustrate what
can be done.)
forall (n : nat) (ns : list nat), In n (map (fun m : nat => m * 0) ns) -> n = 0forall (n : nat) (ns : list nat), In n (map (fun m : nat => m * 0) ns) -> n = 0n:natns:list natH:In n (map (fun m : nat => m * 0) ns)n = 0n:natns:list natH:In n (map (fun m0 : nat => m0 * 0) ns)m:natHm:m * 0 = nn = 0n:natns:list natH:In n (map (fun m0 : nat => m0 * 0) ns)m:natHm:0 = nn = 0reflexivity. Qed.n:natns:list natH:In n (map (fun m0 : nat => m0 * 0) ns)m:natHm:0 = n0 = 0
We will see many more examples in later chapters.
(* ################################################################# *)
Coq's logical core, the Calculus of Inductive
Constructions, differs in some important ways from other formal
systems that are used by mathematicians to write down precise and
rigorous proofs. For example, in the most popular foundation for
paper-and-pencil mathematics, Zermelo-Fraenkel Set Theory (ZFC), a
mathematical object can potentially be a member of many different
sets; a term in Coq's logic, on the other hand, is a member of at
most one type. This difference often leads to slightly different
ways of capturing informal mathematical concepts, but these are,
by and large, about equally natural and easy to work with. For
example, instead of saying that a natural number n belongs to
the set of even numbers, we would say in Coq that even n holds,
where even : nat → Prop is a property describing even numbers.
However, there are some cases where translating standard
mathematical reasoning into Coq can be cumbersome or sometimes
even impossible, unless we enrich the core logic with additional
axioms.
We conclude this chapter with a brief discussion of some of the
most significant differences between the two worlds.
(* ================================================================= *)
The equality assertions that we have seen so far mostly have
concerned elements of inductive types (nat, bool, etc.). But
since Coq's equality operator is polymorphic, these are not the
only possibilities -- in particular, we can write propositions
claiming that two functions are equal to each other:
(fun x : nat => 3 + x) = (fun x : nat => Nat.pred 4 + x)reflexivity. Qed.(fun x : nat => 3 + x) = (fun x : nat => Nat.pred 4 + x)
In common mathematical practice, two functions f and g are
considered equal if they produce the same outputs:
(forall x, f x = g x) -> f = g
This is known as the principle of functional extensionality.
Informally speaking, an "extensional property" is one that
pertains to an object's observable behavior. Thus, functional
extensionality simply means that a function's identity is
completely determined by what we can observe from it -- i.e., in
Coq terms, the results we obtain after applying it.
Functional extensionality is not part of Coq's built-in logic.
This means that some "reasonable" propositions are not provable.
(fun x : nat => x + 1) = (fun x : nat => 1 + x)(* Stuck *) Abort.(fun x : nat => x + 1) = (fun x : nat => 1 + x)
However, we can add functional extensionality to Coq's core using
the Axiom command.
Axiom functional_extensionality : forall {X Y: Type}
{f g : X -> Y},
(forall (x:X), f x = g x) -> f = g.
Using Axiom has the same effect as stating a theorem and
skipping its proof using Admitted, but it alerts the reader that
this isn't just something we're going to come back and fill in
later!
We can now invoke functional extensionality in proofs:
(fun x : nat => x + 1) = (fun x : nat => 1 + x)(fun x : nat => x + 1) = (fun x : nat => 1 + x)forall x : nat, x + 1 = 1 + xapply plus_comm. Qed.x:natx + 1 = 1 + x
Naturally, we must be careful when adding new axioms into Coq's
logic, as they may render it inconsistent -- that is, they may
make it possible to prove every proposition, including False,
2+2=5, etc.!
Unfortunately, there is no simple way of telling whether an axiom
is safe to add: hard work by highly-trained trained experts is
generally required to establish the consistency of any particular
combination of axioms.
Fortunately, it is known that adding functional extensionality, in
particular, is consistent.
To check whether a particular proof relies on any additional
axioms, use the Print Assumptions command.
Exercise: 4 stars, standard (tr_rev_correct)
Fixpoint rev_append {X} (l1 l2 : list X) : list X := match l1 with | [] => l2 | x :: l1' => rev_append l1' (x :: l2) end. Definition tr_rev {X} (l : list X) : list X := rev_append l [].
This version is said to be tail-recursive, because the recursive
call to the function is the last operation that needs to be
performed (i.e., we don't have to execute ++ after the recursive
call); a decent compiler will generate very efficient code in this
case. Prove that the two definitions are indeed equivalent.
(* FILL IN HERE *) Admitted.forall X : Type, tr_rev = rev
☐
(* ================================================================= *)
We've seen two different ways of expressing logical claims in Coq:
with booleans (of type bool), and with propositions (of type
Prop).
For instance, to claim that a number n is even, we can say
either...
... that evenb n evaluates to true...
evenb 42 = truereflexivity. Qed.evenb 42 = true
... or that there exists some k such that n = double k.
exists k : nat, 42 = double kexists k : nat, 42 = double kreflexivity. Qed.42 = double 21
Of course, it would be pretty strange if these two
characterizations of evenness did not describe the same set of
natural numbers! Fortunately, we can prove that they do...
We first need two helper lemmas.
forall k : nat, evenb (double k) = trueforall k : nat, evenb (double k) = truek:natevenb (double k) = trueevenb (double 0) = truek':natIHk':evenb (double k') = trueevenb (double (S k')) = truereflexivity.evenb (double 0) = truek':natIHk':evenb (double k') = trueevenb (double (S k')) = trueapply IHk'. Qed.k':natIHk':evenb (double k') = trueevenb (double k') = true
forall n : nat, exists k : nat, n = (if evenb n then double k else S (double k))(* Hint: Use the [evenb_S] lemma from [Induction.v]. *) (* FILL IN HERE *) Admitted.forall n : nat, exists k : nat, n = (if evenb n then double k else S (double k))
☐
forall n : nat, evenb n = true <-> (exists k : nat, n = double k)forall n : nat, evenb n = true <-> (exists k : nat, n = double k)n:natevenb n = true <-> (exists k : nat, n = double k)n:natevenb n = true -> exists k : nat, n = double kn:nat(exists k : nat, n = double k) -> evenb n = truen:natevenb n = true -> exists k : nat, n = double kn:natH:evenb n = trueexists k : nat, n = double kn:natH:evenb n = truek:natHk:n = (if evenb n then double k else S (double k))exists k0 : nat, n = double k0n:natH:evenb n = truek:natHk:n = (if evenb n then double k else S (double k))exists k0 : nat, (if evenb n then double k else S (double k)) = double k0n:natH:evenb n = truek:natHk:n = (if evenb n then double k else S (double k))exists k0 : nat, double k = double k0reflexivity.n:natH:evenb n = truek:natHk:n = (if evenb n then double k else S (double k))double k = double kn:nat(exists k : nat, n = double k) -> evenb n = truen, k:natHk:n = double kevenb n = trueapply evenb_double. Qed.n, k:natHk:n = double kevenb (double k) = true
In view of this theorem, we say that the boolean computation
evenb n is reflected in the truth of the proposition ∃ k,
n = double k.
Similarly, to state that two numbers n and m are equal, we can
say either
- (1) that n =? m returns true, or
- (2) that n = m.
forall n1 n2 : nat, (n1 =? n2) = true <-> n1 = n2forall n1 n2 : nat, (n1 =? n2) = true <-> n1 = n2n1, n2:nat(n1 =? n2) = true <-> n1 = n2n1, n2:nat(n1 =? n2) = true -> n1 = n2n1, n2:natn1 = n2 -> (n1 =? n2) = trueapply eqb_true.n1, n2:nat(n1 =? n2) = true -> n1 = n2n1, n2:natn1 = n2 -> (n1 =? n2) = truen1, n2:natH:n1 = n2(n1 =? n2) = truen1, n2:natH:n1 = n2(n2 =? n2) = truereflexivity. Qed.n1, n2:natH:n1 = n2true = true
However, even when the boolean and propositional formulations of a
claim are equivalent from a purely logical perspective, they may
not be equivalent operationally.
In the case of even numbers above, when proving the
backwards direction of even_bool_prop (i.e., evenb_double,
going from the propositional to the boolean claim), we used a
simple induction on k. On the other hand, the converse (the
evenb_double_conv exercise) required a clever generalization,
since we can't directly prove
(evenb n = true) → (∃ k, n = double k).
For these examples, the propositional claims are more useful than
their boolean counterparts, but this is not always the case. For
instance, we cannot test whether a general proposition is true or
not in a function definition; as a consequence, the following code
fragment is rejected:
Coq complains that n = 2 has type Prop, while it expects
an element of bool (or some other inductive type with two
elements). The reason for this error message has to do with the
computational nature of Coq's core language, which is designed
so that every function that it can express is computable and
total. One reason for this is to allow the extraction of
executable programs from Coq developments. As a consequence,
Prop in Coq does not have a universal case analysis operation
telling whether any given proposition is true or false, since such
an operation would allow us to write non-computable functions.
Although general non-computable properties cannot be phrased as
boolean computations, it is worth noting that even many
computable properties are easier to express using Prop than
bool, since recursive function definitions are subject to
significant restrictions in Coq. For instance, the next chapter
shows how to define the property that a regular expression matches
a given string using Prop. Doing the same with bool would
amount to writing a regular expression matcher, which would be
more complicated, harder to understand, and harder to reason
about.
Conversely, an important side benefit of stating facts using
booleans is enabling some proof automation through computation
with Coq terms, a technique known as proof by
reflection. Consider the following statement:
exists k : nat, 1000 = double k
The most direct proof of this fact is to give the value of k
explicitly.
exists k : nat, 1000 = double kreflexivity. Qed.1000 = double 500
On the other hand, the proof of the corresponding boolean
statement is even simpler:
evenb 1000 = truereflexivity. Qed.evenb 1000 = true
What is interesting is that, since the two notions are equivalent,
we can use the boolean formulation to prove the other one without
mentioning the value 500 explicitly:
exists k : nat, 1000 = double kexists k : nat, 1000 = double kreflexivity. Qed.evenb 1000 = true
Although we haven't gained much in terms of proof-script
size in this case, larger proofs can often be made considerably
simpler by the use of reflection. As an extreme example, the Coq
proof of the famous 4-color theorem uses reflection to reduce
the analysis of hundreds of different cases to a boolean
computation.
Another notable difference is that the negation of a "boolean
fact" is straightforward to state and prove: simply flip the
expected boolean result.
evenb 1001 = false(* WORKED IN CLASS *) reflexivity. Qed.evenb 1001 = false
In contrast, propositional negation may be more difficult
to grasp.
~ (exists k : nat, 1001 = double k)(* WORKED IN CLASS *)~ (exists k : nat, 1001 = double k)evenb 1001 <> trueevenb 1001 = true -> Falsefalse = true -> Falsediscriminate H. Qed.H:false = trueFalse
Equality provides a complementary example: knowing that
n =? m = true is generally of little direct help in the middle
of a proof involving n and m; however, if we convert the
statement to the equivalent form n = m, we can rewrite with it.
forall n m p : nat, (n =? m) = true -> (n + p =? m + p) = true(* WORKED IN CLASS *)forall n m p : nat, (n =? m) = true -> (n + p =? m + p) = truen, m, p:natH:(n =? m) = true(n + p =? m + p) = truen, m, p:natH:n = m(n + p =? m + p) = truen, m, p:natH:n = m(m + p =? m + p) = truereflexivity. Qed.n, m, p:natH:n = mm + p = m + p
We won't cover reflection in much detail, but it serves as a good
example showing the complementary strengths of booleans and
general propositions.
Exercise: 2 stars, standard (logical_connectives)
forall b1 b2 : bool, b1 && b2 = true <-> b1 = true /\ b2 = true(* FILL IN HERE *) Admitted.forall b1 b2 : bool, b1 && b2 = true <-> b1 = true /\ b2 = trueforall b1 b2 : bool, b1 || b2 = true <-> b1 = true \/ b2 = true(* FILL IN HERE *) Admitted.forall b1 b2 : bool, b1 || b2 = true <-> b1 = true \/ b2 = true
☐
Exercise: 1 star, standard (eqb_neq)
forall x y : nat, (x =? y) = false <-> x <> y(* FILL IN HERE *) Admitted.forall x y : nat, (x =? y) = false <-> x <> y
☐
Exercise: 3 stars, standard (eqb_list)
Admitted.eqb_list:forall A0 : Type, (A0 -> A0 -> bool) -> list A0 -> list A0 -> boolA:Typeeqb:A -> A -> booll1, l2:list Aboolforall (A : Type) (eqb : A -> A -> bool), (forall a1 a2 : A, eqb a1 a2 = true <-> a1 = a2) -> forall l1 l2 : list A, eqb_list eqb l1 l2 = true <-> l1 = l2(* FILL IN HERE *) Admitted.forall (A : Type) (eqb : A -> A -> bool), (forall a1 a2 : A, eqb a1 a2 = true <-> a1 = a2) -> forall l1 l2 : list A, eqb_list eqb l1 l2 = true <-> l1 = l2
☐
Exercise: 2 stars, standard, recommended (All_forallb)
Fixpoint forallb {X : Type} (test : X -> bool) (l : list X) : bool :=
match l with
| [] => true
| x :: l' => andb (test x) (forallb test l')
end.
Prove the theorem below, which relates forallb to the All
property of the above exercise.
forall (X : Type) (test : X -> bool) (l : list X), forallb test l = true <-> All (fun x : X => test x = true) l(* FILL IN HERE *) Admitted.forall (X : Type) (test : X -> bool) (l : list X), forallb test l = true <-> All (fun x : X => test x = true) l
Are there any important properties of the function forallb which
are not captured by this specification?
(* FILL IN HERE
[] *)
(* ================================================================= *)
We have seen that it is not possible to test whether or not a
proposition P holds while defining a Coq function. You may be
surprised to learn that a similar restriction applies to proofs!
In other words, the following intuitive reasoning principle is not
derivable in Coq:
Definition excluded_middle := forall P : Prop,
P \/ ~ P.
To understand operationally why this is the case, recall
that, to prove a statement of the form P ∨ Q, we use the left
and right tactics, which effectively require knowing which side
of the disjunction holds. But the universally quantified P in
excluded_middle is an arbitrary proposition, which we know
nothing about. We don't have enough information to choose which
of left or right to apply, just as Coq doesn't have enough
information to mechanically decide whether P holds or not inside
a function.
However, if we happen to know that P is reflected in some
boolean term b, then knowing whether it holds or not is trivial:
we just have to check the value of b.
forall (P : Prop) (b : bool), P <-> b = true -> P \/ ~ Pforall (P : Prop) (b : bool), P <-> b = true -> P \/ ~ PP:PropH:P <-> true = trueP \/ ~ PP:PropH:P <-> false = trueP \/ ~ PP:PropH:P <-> true = trueP \/ ~ PP:PropH:P <-> true = truePreflexivity.P:PropH:P <-> true = truetrue = trueP:PropH:P <-> false = trueP \/ ~ PP:PropH:P <-> false = true~ PP:PropH:P <-> false = truefalse <> truediscriminate contra. Qed.P:PropH:P <-> false = truecontra:false = trueFalse
In particular, the excluded middle is valid for equations n = m,
between natural numbers n and m.
forall n m : nat, n = m \/ n <> mforall n m : nat, n = m \/ n <> mn, m:natn = m \/ n <> mn, m:natn = m <-> (n =? m) = trueapply eqb_eq. Qed.n, m:nat(n =? m) = true <-> n = m
It may seem strange that the general excluded middle is not
available by default in Coq; after all, any given claim must be
either true or false. Nonetheless, there is an advantage in not
assuming the excluded middle: statements in Coq can make stronger
claims than the analogous statements in standard mathematics.
Notably, if there is a Coq proof of ∃ x, P x, it is
possible to explicitly exhibit a value of x for which we can
prove P x -- in other words, every proof of existence is
necessarily constructive.
Logics like Coq's, which do not assume the excluded middle, are
referred to as constructive logics.
More conventional logical systems such as ZFC, in which the
excluded middle does hold for arbitrary propositions, are referred
to as classical.
The following example illustrates why assuming the excluded middle
may lead to non-constructive proofs:
Claim: There exist irrational numbers a and b such that a ^
b is rational.
Proof: It is not difficult to show that sqrt 2 is irrational.
If sqrt 2 ^ sqrt 2 is rational, it suffices to take a = b =
sqrt 2 and we are done. Otherwise, sqrt 2 ^ sqrt 2 is
irrational. In this case, we can take a = sqrt 2 ^ sqrt 2 and
b = sqrt 2, since a ^ b = sqrt 2 ^ (sqrt 2 × sqrt 2) = sqrt 2 ^
2 = 2. ☐
Do you see what happened here? We used the excluded middle to
consider separately the cases where sqrt 2 ^ sqrt 2 is rational
and where it is not, without knowing which one actually holds!
Because of that, we wind up knowing that such a and b exist
but we cannot determine what their actual values are (at least,
using this line of argument).
As useful as constructive logic is, it does have its limitations:
There are many statements that can easily be proven in classical
logic but that have much more complicated constructive proofs, and
there are some that are known to have no constructive proof at
all! Fortunately, like functional extensionality, the excluded
middle is known to be compatible with Coq's logic, allowing us to
add it safely as an axiom. However, we will not need to do so in
this book: the results that we cover can be developed entirely
within constructive logic at negligible extra cost.
It takes some practice to understand which proof techniques must
be avoided in constructive reasoning, but arguments by
contradiction, in particular, are infamous for leading to
non-constructive proofs. Here's a typical example: suppose that
we want to show that there exists x with some property P,
i.e., such that P x. We start by assuming that our conclusion
is false; that is, ¬ ∃ x, P x. From this premise, it is not
hard to derive ∀ x, ¬ P x. If we manage to show that this
intermediate fact results in a contradiction, we arrive at an
existence proof without ever exhibiting a value of x for which
P x holds!
The technical flaw here, from a constructive standpoint, is that
we claimed to prove ∃ x, P x using a proof of
¬ ¬ (∃ x, P x). Allowing ourselves to remove double
negations from arbitrary statements is equivalent to assuming the
excluded middle, as shown in one of the exercises below. Thus,
this line of reasoning cannot be encoded in Coq without assuming
additional axioms.
Exercise: 3 stars, standard (excluded_middle_irrefutable)
forall P : Prop, ~ ~ (P \/ ~ P)(* FILL IN HERE *) Admitted.forall P : Prop, ~ ~ (P \/ ~ P)
☐
Exercise: 3 stars, advanced (not_exists_dist)
excluded_middle -> forall (X : Type) (P : X -> Prop), ~ (exists x : X, ~ P x) -> forall x : X, P x(* FILL IN HERE *) Admitted.excluded_middle -> forall (X : Type) (P : X -> Prop), ~ (exists x : X, ~ P x) -> forall x : X, P x
☐
Exercise: 5 stars, standard, optional (classical_axioms)
Definition peirce := forall P Q: Prop, ((P->Q)->P)->P. Definition double_negation_elimination := forall P:Prop, ~~P -> P. Definition de_morgan_not_and_not := forall P Q:Prop, ~(~P /\ ~Q) -> P\/Q. Definition implies_to_or := forall P Q:Prop, (P->Q) -> (~P\/Q). (* FILL IN HERE [] *) (* Wed Jan 9 12:02:45 EST 2019 *)