LogicLogic in Coq
Set Warnings "-notation-overridden,-parsing,-deprecated-hint-without-locality".
From LF Require Export Tactics.
From LF Require Export Tactics.
So far, we have seen...
In this chapter we will introduce several more flavors of both
propositions and proofs.
Like everything in Coq, propositions have a type:
- propositions: factual claims
- equality propositions (e_{1} = e_{2})
- implications (P → Q)
- quantified propositions (∀ x, P)
- proofs: ways of presenting evidence for the truth of a proposition
Check (3 = 3) : Prop.
Check (∀ n m : nat, n + m = m + n) : Prop.
Check (∀ n m : nat, n + m = m + n) : Prop.
Note that all syntactically well-formed propositions have type Prop in Coq, regardless of whether they are true.
Check 2 = 2 : Prop.
Check 3 = 2 : Prop.
Check ∀ n : nat, n = 2 : Prop.
Check 3 = 2 : Prop.
Check ∀ n : nat, n = 2 : Prop.
So far, we've seen one primary place that propositions can appear: in Theorem (and Lemma and Example) declarations.
Theorem plus_2_2_is_4 :
2 + 2 = 4.
Proof. reflexivity. Qed.
2 + 2 = 4.
Proof. reflexivity. Qed.
Definition plus_claim : Prop := 2 + 2 = 4.
Check plus_claim : Prop.
Theorem plus_claim_is_true :
plus_claim.
Proof. reflexivity. Qed.
Check plus_claim : Prop.
Theorem plus_claim_is_true :
plus_claim.
Proof. reflexivity. Qed.
We can also write parameterized propositions -- that is, functions that take arguments of some type and return a proposition.
Definition is_three (n : nat) : Prop :=
n = 3.
Check is_three : nat → Prop.
n = 3.
Check is_three : nat → Prop.
In Coq, functions that return propositions are said to define properties of their arguments.
Definition injective {A B} (f : A → B) :=
∀ x y : A, f x = f y → x = y.
Lemma succ_inj : injective S.
Proof.
intros n m H. injection H as H_{1}. apply H_{1}.
Qed.
∀ x y : A, f x = f y → x = y.
Lemma succ_inj : injective S.
Proof.
intros n m H. injection H as H_{1}. apply H_{1}.
Qed.
The equality operator = is a (binary) function that returns a Prop.
Check @eq : ∀ A : Type, A → A → Prop.
What is the type of the following expression?
pred (S O) = O (1) Prop
(2) nat→Prop
(3) ∀ n:nat, Prop
(4) nat→nat
(5) Not typeable
pred (S O) = O (1) Prop
What is the type of the following expression?
∀ n:nat, pred (S n) = n (1) Prop
(2) nat→Prop
(3) ∀ n:nat, Prop
(4) nat→nat
(5) Not typeable
∀ n:nat, pred (S n) = n (1) Prop
What is the type of the following expression?
∀ n:nat, S (pred n) = n (1) Prop
(2) nat→Prop
(3) nat→nat
(4) Not typeable
∀ n:nat, S (pred n) = n (1) Prop
What is the type of the following expression?
∀ n:nat, S (pred n) (1) Prop
(2) nat→Prop
(3) nat→nat
(4) Not typeable
∀ n:nat, S (pred n) (1) Prop
What is the type of the following expression?
fun n:nat ⇒ S (pred n) (1) Prop
(2) nat→Prop
(3) nat→nat
(4) Not typeable
fun n:nat ⇒ S (pred n) (1) Prop
What is the type of the following expression?
fun n:nat ⇒ S (pred n) = n (1) Prop
(2) nat→Prop
(3) nat→nat
(4) Not typeable
fun n:nat ⇒ S (pred n) = n (1) Prop
Which of the following is not a proposition?
(1) 3 + 2 = 4
(2) 3 + 2 = 5
(3) 3 + 2 =? 5
(4) ((3+2) =? 4) = false
(5) ∀ n, (((3+2) =? n) = true) → n = 5
(6) All of these are propositions
Logical Connectives
Conjunction
Example and_example : 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:
Proof.
split.
- (* 3 + 4 = 7 *) reflexivity.
- (* 2 * 2 = 4 *) reflexivity.
Qed.
split.
- (* 3 + 4 = 7 *) reflexivity.
- (* 2 * 2 = 4 *) reflexivity.
Qed.
For any propositions A and B, if we assume that A is true and that B is true, we can conclude that A ∧ B is also true.
Lemma and_intro : ∀ A B : Prop, A → B → A ∧ B.
Proof.
intros A B HA HB. split.
- apply HA.
- apply HB.
Qed.
Example and_exercise :
∀ n m : nat, n + m = 0 → n = 0 ∧ m = 0.
Proof.
(* WORK IN CLASS *) Admitted.
Proof.
intros A B HA HB. split.
- apply HA.
- apply HB.
Qed.
Example and_exercise :
∀ n m : nat, n + m = 0 → n = 0 ∧ m = 0.
Proof.
(* WORK IN CLASS *) Admitted.
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.
Lemma and_example2 :
∀ n m : nat, n = 0 ∧ m = 0 → n + m = 0.
Proof.
(* WORK IN CLASS *) Admitted.
∀ n m : nat, n = 0 ∧ m = 0 → n + m = 0.
Proof.
(* WORK IN CLASS *) Admitted.
As usual, we can also destruct H right when we introduce it, instead of introducing and then destructing it:
Lemma and_example2' :
∀ n m : nat, n = 0 ∧ m = 0 → n + m = 0.
Proof.
intros n m [Hn Hm].
rewrite Hn. rewrite Hm.
reflexivity.
Qed.
∀ n m : nat, n = 0 ∧ m = 0 → n + m = 0.
Proof.
intros n m [Hn Hm].
rewrite Hn. rewrite Hm.
reflexivity.
Qed.
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:
Lemma and_example2'' :
∀ n m : nat, n = 0 → m = 0 → n + m = 0.
Proof.
intros n m Hn Hm.
rewrite Hn. rewrite Hm.
reflexivity.
Qed.
∀ n m : nat, n = 0 → m = 0 → n + m = 0.
Proof.
intros n m Hn Hm.
rewrite Hn. rewrite Hm.
reflexivity.
Qed.
For the present example, both ways work. But in other
situations we may wind up with a conjunctive hypothesis in the
middle of a proof...
Lemma and_example3 :
∀ n m : nat, n + m = 0 → n × m = 0.
Proof.
(* WORK IN CLASS *) Admitted.
∀ n m : nat, n + m = 0 → n × m = 0.
Proof.
(* WORK IN CLASS *) Admitted.
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.
Check and : Prop → Prop → Prop.
Disjunction
To use a disjunctive hypothesis in a proof, we proceed by case analysis -- which, as with other data types like nat, can be done explicitly with destruct or implicitly with an intros pattern:
Lemma factor_is_O:
∀ n m : nat, n = 0 ∨ m = 0 → n × m = 0.
Proof.
(* This pattern implicitly does case analysis on
n = 0 ∨ m = 0 *)
intros n m [Hn | Hm].
- (* Here, n = 0 *)
rewrite Hn. reflexivity.
- (* Here, m = 0 *)
rewrite Hm. rewrite <- mult_n_O.
reflexivity.
Qed.
∀ n m : nat, n = 0 ∨ m = 0 → n × m = 0.
Proof.
(* This pattern implicitly does case analysis on
n = 0 ∨ m = 0 *)
intros n m [Hn | Hm].
- (* Here, n = 0 *)
rewrite Hn. reflexivity.
- (* Here, m = 0 *)
rewrite Hm. rewrite <- mult_n_O.
reflexivity.
Qed.
We can see in this example that, when we perform case
analysis on a disjunction A ∨ B, we must separately satisfy two
proof obligations, each showing that the conclusion holds under a
different assumption -- A in the first subgoal and B in the
second. Note that the case analysis pattern [Hn | Hm] allows
us to name the hypotheses that are generated for the subgoals.
Conversely, to show that a disjunction holds, it suffices to show that one of its sides holds. This can be 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...
Lemma or_intro_l : ∀ A B : Prop, A → A ∨ B.
Proof.
intros A B HA.
left.
apply HA.
Qed.
Proof.
intros A B HA.
left.
apply HA.
Qed.
Lemma zero_or_succ :
∀ n : nat, n = 0 ∨ n = S (pred n).
Proof.
(* WORK IN CLASS *) Admitted.
∀ n : nat, n = 0 ∨ n = S (pred n).
Proof.
(* WORK IN CLASS *) Admitted.
Lemma mult_is_O :
∀ n m, n × m = 0 → n = 0 ∨ m = 0.
Proof.
(* FILL IN HERE *) Admitted.
☐
∀ n m, n × m = 0 → n = 0 ∨ m = 0.
Proof.
(* FILL IN HERE *) Admitted.
☐
Theorem or_commut : ∀ P Q : Prop,
P ∨ Q → Q ∨ P.
Proof.
(* FILL IN HERE *) Admitted.
☐
P ∨ Q → Q ∨ P.
Proof.
(* FILL IN HERE *) Admitted.
☐
Falsehood and Negation
So far, we have mostly been concerned with proving "positive" statements -- addition is commutative, appending lists is associative, etc. Of course, we may also be interested in negative results, demonstrating that some given proposition is not true. Such statements are expressed with the logical negation operator ¬.To see how negation works, recall the principle of explosion from the Tactics chapter, which asserts that, if we assume a contradiction, then any other proposition can be derived.
Module NotPlayground.
Definition not (P:Prop) := P → False.
Notation "~ x" := (not x) : type_scope.
Check not : Prop → Prop.
End NotPlayground.
Definition not (P:Prop) := P → False.
Notation "~ x" := (not x) : type_scope.
Check not : Prop → Prop.
End NotPlayground.
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:
Theorem ex_falso_quodlibet : ∀ (P:Prop),
False → P.
Proof.
(* WORK IN CLASS *) Admitted.
False → P.
Proof.
(* WORK IN CLASS *) Admitted.
Inequality is a frequent enough form of negated statement that there is a special notation for it, x ≠ y:
Notation "x <> y" := (~(x = y)).
Theorem zero_not_one : 0 ≠ 1.
Proof.
unfold not.
intros contra.
discriminate contra.
Qed.
Proof.
unfold not.
intros contra.
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 make Coq understand it! Here are proofs of a few familiar facts to get you warmed up.
Theorem not_False :
¬False.
Proof.
unfold not. intros H. destruct H. Qed.
¬False.
Proof.
unfold not. intros H. destruct H. Qed.
Theorem contradiction_implies_anything : ∀ P Q : Prop,
(P ∧ ¬P) → Q.
Proof.
(* WORK IN CLASS *) Admitted.
Theorem double_neg : ∀ P : Prop,
P → ~~P.
Proof.
(* WORK IN CLASS *) Admitted.
(P ∧ ¬P) → Q.
Proof.
(* WORK IN CLASS *) Admitted.
Theorem double_neg : ∀ P : Prop,
P → ~~P.
Proof.
(* WORK IN CLASS *) Admitted.
Since inequality involves a negation, it also requires a little practice to be able to work with it fluently. Here is one useful trick:
Theorem not_true_is_false : ∀ b : bool,
b ≠ true → b = false.
Proof.
intros b H.
destruct b eqn:HE.
- (* b = true *)
unfold not in H.
apply ex_falso_quodlibet.
apply H. reflexivity.
- (* b = false *)
reflexivity.
Qed.
b ≠ true → b = false.
Proof.
intros b H.
destruct b eqn:HE.
- (* b = true *)
unfold not in H.
apply ex_falso_quodlibet.
apply H. reflexivity.
- (* b = false *)
reflexivity.
Qed.
To prove the following proposition, which tactics will we need
besides intros and apply?
∀ X, ∀ a b : X, (a=b) ∧ (a≠b) → False. (1) destruct, unfold, left and right
(2) destruct and unfold
(3) only destruct
(4) left and/or right
(5) only unfold
(6) none of the above
∀ X, ∀ a b : X, (a=b) ∧ (a≠b) → False. (1) destruct, unfold, left and right
Lemma quiz1: ∀ X, ∀ a b : X, (a=b) ∧ (a≠b) → False.
Proof.
intros X a b [H_{0} H_{1}]. apply H_{1} in H_{0}. apply H_{0}.
Qed.
Proof.
intros X a b [H_{0} H_{1}]. apply H_{1} in H_{0}. apply H_{0}.
Qed.
To prove the following proposition, which tactics will we
need besides intros and apply?
∀ P Q : Prop, P ∨ Q → ~~(P ∨ Q). (1) destruct, unfold, left and right
(2) destruct and unfold
(3) only destruct
(4) left and/or right
(5) only unfold
(6) none of the above
∀ P Q : Prop, P ∨ Q → ~~(P ∨ Q). (1) destruct, unfold, left and right
Lemma quiz2 : ∀ P Q : Prop, P ∨ Q → ~~(P ∨ Q).
Proof.
intros P Q H H_{1}. apply H_{1} in H. apply H.
Qed.
Proof.
intros P Q H H_{1}. apply H_{1} in H. apply H.
Qed.
To prove the following proposition, which tactics will we
need besides intros and apply?
∀ A B: Prop, A → (A ∨ ~~B). (1) destruct, unfold, left and right
(2) destruct and unfold
(3) only destruct
(4) left and/or right
(5) only unfold
(6) none of the above
∀ A B: Prop, A → (A ∨ ~~B). (1) destruct, unfold, left and right
Lemma quiz3 : ∀ A B: Prop, A → (A ∨ ~~B).
Proof.
intros P Q H. left. apply H.
Qed.
Proof.
intros P Q H. left. apply H.
Qed.
To prove the following proposition, which tactics will we need
besides intros and apply?
∀ P Q: Prop, P ∨ Q → ~~P ∨ ~~Q. (1) destruct, unfold, left and right
(2) destruct and unfold
(3) only destruct
(4) left and/or right
(5) only unfold
(6) none of the above
∀ P Q: Prop, P ∨ Q → ~~P ∨ ~~Q. (1) destruct, unfold, left and right
Lemma quiz4 : ∀ P Q: Prop, P ∨ Q → ~~P ∨ ~~Q.
Proof.
intros P Q [H_{0} | H_{0}].
- (* left *)
left. intros H_{1}. apply H_{1} in H_{0}. apply H_{0}.
- (* right *)
right. intros H_{1}. apply H_{1} in H_{0}. apply H_{0}.
Qed.
Proof.
intros P Q [H_{0} | H_{0}].
- (* left *)
left. intros H_{1}. apply H_{1} in H_{0}. apply H_{0}.
- (* right *)
right. intros H_{1}. apply H_{1} in H_{0}. apply H_{0}.
Qed.
To prove the following proposition, which tactics will we need
besides intros and apply?
∀ A : Prop, 1=0 → (A ∨ ¬A). (1) discriminate, unfold, left and right
(2) discriminate and unfold
(3) only discriminate
(4) left and/or right
(5) only unfold
(6) none of the above
∀ A : Prop, 1=0 → (A ∨ ¬A). (1) discriminate, unfold, left and right
Lemma quiz5 : ∀ A : Prop, 1=0 → (A ∨ ¬A).
Proof.
intros P H. discriminate H.
Qed.
Proof.
intros P H. discriminate H.
Qed.
Truth
Lemma True_is_true : True.
Proof. apply I. Qed.
Proof. apply I. Qed.
Unlike False, which is used extensively, True is used
relatively rarely, since it is trivial (and therefore
uninteresting) to prove as a goal, and conversely it provides no
interesting information when used as a hypothesis.
The handy "if and only if" connective, which asserts that two
propositions have the same truth value, is simply the conjunction
of two implications.
Logical Equivalence
Module IffPlayground.
Definition iff (P Q : Prop) := (P → Q) ∧ (Q → P).
Notation "P <-> Q" := (iff P Q)
(at level 95, no associativity)
: type_scope.
End IffPlayground.
Definition iff (P Q : Prop) := (P → Q) ∧ (Q → P).
Notation "P <-> Q" := (iff P Q)
(at level 95, no associativity)
: type_scope.
End IffPlayground.
Theorem iff_sym : ∀ P Q : Prop,
(P ↔ Q) → (Q ↔ P).
Proof.
(* WORK IN CLASS *) Admitted.
Lemma not_true_iff_false : ∀ b,
b ≠ true ↔ b = false.
Proof.
(* WORK IN CLASS *) Admitted.
(P ↔ Q) → (Q ↔ P).
Proof.
(* WORK IN CLASS *) Admitted.
Lemma not_true_iff_false : ∀ b,
b ≠ true ↔ b = false.
Proof.
(* WORK IN CLASS *) Admitted.
Lemma apply_iff_example1:
∀ P Q R : Prop, (P ↔ Q) → (Q → R) → (P → R).
intros P Q R Hiff H HP. apply H. apply Hiff. apply HP.
Qed.
Lemma apply_iff_example2:
∀ P Q R : Prop, (P ↔ Q) → (P → R) → (Q → R).
intros P Q R Hiff H HQ. apply H. apply Hiff. apply HQ.
Qed.
∀ P Q R : Prop, (P ↔ Q) → (Q → R) → (P → R).
intros P Q R Hiff H HP. apply H. apply Hiff. apply HP.
Qed.
Lemma apply_iff_example2:
∀ P Q R : Prop, (P ↔ Q) → (P → R) → (Q → R).
intros P Q R Hiff H HQ. apply H. apply Hiff. apply HQ.
Qed.
Exercise: 1 star, standard, optional (iff_properties)
Using the above proof that ↔ is symmetric (iff_sym) as a guide, prove that it is also reflexive and transitive.
Theorem iff_refl : ∀ P : Prop,
P ↔ P.
Proof.
(* FILL IN HERE *) Admitted.
Theorem iff_trans : ∀ P Q R : Prop,
(P ↔ Q) → (Q ↔ R) → (P ↔ R).
Proof.
(* FILL IN HERE *) Admitted.
☐
P ↔ P.
Proof.
(* FILL IN HERE *) Admitted.
Theorem iff_trans : ∀ P Q R : Prop,
(P ↔ Q) → (Q ↔ R) → (P ↔ R).
Proof.
(* FILL IN HERE *) Admitted.
☐
Setoids and Logical Equivalence
From Coq Require Import Setoids.Setoid.
A "setoid" is a set equipped with an equivalence relation,
such as = or ↔.
Here is a simple example demonstrating how these tactics work with
iff. First, let's prove a couple of basic iff equivalences.
Lemma mul_eq_0 : ∀ n m, n × m = 0 ↔ n = 0 ∨ m = 0.
Theorem or_assoc :
∀ P Q R : Prop, P ∨ (Q ∨ R) ↔ (P ∨ Q) ∨ R.
Lemma mul_eq_0_ternary :
∀ n m p, n × m × p = 0 ↔ n = 0 ∨ m = 0 ∨ p = 0.
Proof.
intros n m p.
rewrite mul_eq_0. rewrite mul_eq_0. rewrite or_assoc.
reflexivity.
Qed.
Proof.
split.
- apply mult_is_O.
- apply factor_is_O.
Qed.
split.
- apply mult_is_O.
- apply factor_is_O.
Qed.
Theorem or_assoc :
∀ P Q R : Prop, P ∨ (Q ∨ R) ↔ (P ∨ Q) ∨ R.
Proof.
intros P Q R. split.
- intros [H | [H | H]].
+ left. left. apply H.
+ left. right. apply H.
+ right. apply H.
- intros [[H | H] | H].
+ left. apply H.
+ right. left. apply H.
+ right. right. apply H.
Qed.
intros P Q R. split.
- intros [H | [H | H]].
+ left. left. apply H.
+ left. right. apply H.
+ right. apply H.
- intros [[H | H] | H].
+ left. apply H.
+ right. left. apply H.
+ right. right. apply H.
Qed.
Lemma mul_eq_0_ternary :
∀ n m p, n × m × p = 0 ↔ n = 0 ∨ m = 0 ∨ p = 0.
Proof.
intros n m p.
rewrite mul_eq_0. rewrite mul_eq_0. rewrite or_assoc.
reflexivity.
Qed.
Existential Quantification
Definition Even x := ∃ n : nat, x = double n.
Lemma four_is_Even : Even 4.
Proof.
unfold Even. ∃ 2. reflexivity.
Qed.
Lemma four_is_Even : Even 4.
Proof.
unfold Even. ∃ 2. reflexivity.
Qed.
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.
Theorem exists_example_2 : ∀ n,
(∃ m, n = 4 + m) →
(∃ o, n = 2 + o).
Proof.
(* WORK IN CLASS *) Admitted.
(∃ m, n = 4 + m) →
(∃ o, n = 2 + o).
Proof.
(* WORK IN CLASS *) Admitted.
Theorem leb_plus_exists : ∀ n m, n <=? m = true → ∃ x, m = n+x.
Proof.
(* FILL IN HERE *) Admitted.
Theorem plus_exists_leb : ∀ n m, (∃ x, m = n+x) → n <=? m = true.
Proof.
(* FILL IN HERE *) Admitted.
☐
Proof.
(* FILL IN HERE *) Admitted.
Theorem plus_exists_leb : ∀ n m, (∃ x, m = n+x) → n <=? m = true.
Proof.
(* FILL IN HERE *) Admitted.
☐
Programming with Propositions
- If l is the empty list, then x cannot occur in 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 it is equal to x' or if it occurs in l'.
Fixpoint In {A : Type} (x : A) (l : list A) : Prop :=
match l with
| [] ⇒ False
| x' :: l' ⇒ x' = x ∨ In x l'
end.
match l with
| [] ⇒ False
| x' :: l' ⇒ x' = x ∨ In x l'
end.
Example In_example_1 : In 4 [1; 2; 3; 4; 5].
Proof.
(* WORK IN CLASS *) Admitted.
Example In_example_2 :
∀ n, In n [2; 4] →
∃ n', n = 2 × n'.
Proof.
(* WORK IN CLASS *) Admitted.
Proof.
(* WORK IN CLASS *) Admitted.
Example In_example_2 :
∀ n, In n [2; 4] →
∃ n', n = 2 × n'.
Proof.
(* WORK IN CLASS *) Admitted.
Theorem In_map :
∀ (A B : Type) (f : A → B) (l : list A) (x : A),
In x l →
In (f x) (map f l).
∀ (A B : Type) (f : A → B) (l : list A) (x : A),
In x l →
In (f x) (map f l).
Proof.
intros A B f l x.
induction l as [|x' l' IHl'].
- (* l = nil, contradiction *)
simpl. intros [].
- (* l = x' :: l' *)
simpl. intros [H | H].
+ rewrite H. left. reflexivity.
+ right. apply IHl'. apply H.
Qed.
intros A B f l x.
induction l as [|x' l' IHl'].
- (* l = nil, contradiction *)
simpl. intros [].
- (* l = x' :: l' *)
simpl. intros [H | H].
+ rewrite H. left. reflexivity.
+ right. apply IHl'. apply H.
Qed.
Applying Theorems to Arguments
Check plus : nat → nat → nat.
Check add_comm : ∀ n m : nat, n + m = m + n.
Check add_comm : ∀ n m : nat, n + m = m + n.
Coq checks the statement of the add_comm theorem (or prints
it for us, if we leave off the part beginning with the colon) in
the same way that it checks the type of any term (e.g., plus)
that we ask it to Check.
Why?
Similarly, the statement of a theorem tells us what we can use
that theorem for.
This is often handy in proof scripts -- e.g., suppose we want too
prove the following:
The reason is that the identifier add_comm actually refers to a proof object, which 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 proposition that it is a proof of.
The type of an ordinary function tells us what we can do with it.
- 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.
- if we have a term of type n = m → n + n = m + m and we provide it two numbers n and m plus an "argument" of type n = m, we can derive n + n = m + m.
Coq actually allows us to apply a theorem as if it were a function.
Lemma add_comm3 :
∀ x y z, x + (y + z) = (z + y) + x.
∀ x y z, x + (y + z) = (z + y) + x.
It appears at first sight that we ought to be able to prove this
by rewriting with add_comm twice to make the two sides match.
The problem, however, is that the second rewrite will undo the
effect of the first.
Proof.
intros x y z.
rewrite add_comm.
rewrite add_comm.
(* We are back where we started... *)
Abort.
intros x y z.
rewrite add_comm.
rewrite add_comm.
(* We are back where we started... *)
Abort.
We can fix this by applying add_comm to the arguments we want it be to instantiated with. Then the rewrite can only happen in one place.
Lemma add_comm3_take3 :
∀ x y z, x + (y + z) = (z + y) + x.
Proof.
intros x y z.
rewrite add_comm.
rewrite (add_comm y z).
reflexivity.
Qed.
∀ x y z, x + (y + z) = (z + y) + x.
Proof.
intros x y z.
rewrite add_comm.
rewrite (add_comm y z).
reflexivity.
Qed.
Let's see another example of using a theorem like a function.
Theorem in_not_nil :
∀ A (x : A) (l : list A), In x l → l ≠ [].
∀ A (x : A) (l : list A), In x l → l ≠ [].
Proof.
intros A x l H. unfold not. intro Hl.
rewrite Hl in H.
simpl in H.
apply H.
Qed.
intros A x l H. unfold not. intro Hl.
rewrite Hl in H.
simpl in H.
apply H.
Qed.
We should be able to use this theorem to prove the special case
where x is 42. However, naively, the tactic apply in_not_nil
will fail because it cannot infer the value of x.
Lemma in_not_nil_42 :
∀ l : list nat, In 42 l → l ≠ [].
Proof.
intros l H.
Fail apply in_not_nil.
Abort.
∀ l : list nat, In 42 l → l ≠ [].
Proof.
intros l H.
Fail apply in_not_nil.
Abort.
Lemma in_not_nil_42_take2 :
∀ l : list nat, In 42 l → l ≠ [].
Proof.
intros l H.
apply in_not_nil with (x := 42).
apply H.
Qed.
∀ l : list nat, In 42 l → l ≠ [].
Proof.
intros l H.
apply in_not_nil with (x := 42).
apply H.
Qed.
Lemma in_not_nil_42_take3 :
∀ l : list nat, In 42 l → l ≠ [].
Proof.
intros l H.
apply in_not_nil in H.
apply H.
Qed.
∀ l : list nat, In 42 l → l ≠ [].
Proof.
intros l H.
apply in_not_nil in H.
apply H.
Qed.
Lemma in_not_nil_42_take4 :
∀ l : list nat, In 42 l → l ≠ [].
Proof.
intros l H.
apply (in_not_nil nat 42).
apply H.
Qed.
∀ l : list nat, In 42 l → l ≠ [].
Proof.
intros l H.
apply (in_not_nil nat 42).
apply H.
Qed.
Lemma in_not_nil_42_take5 :
∀ l : list nat, In 42 l → l ≠ [].
Proof.
intros l H.
apply (in_not_nil _ _ _ H).
Qed.
∀ l : list nat, In 42 l → l ≠ [].
Proof.
intros l H.
apply (in_not_nil _ _ _ H).
Qed.
Lemma quiz : ∀ a b : nat,
a = b → b = 42 →
(∀ (X : Type) (n m o : X),
n = m → m = o → n = o) →
True.
Proof.
intros a b H_{1} H_{2} trans_eq.
a = b → b = 42 →
(∀ (X : Type) (n m o : X),
n = m → m = o → n = o) →
True.
Proof.
intros a b H_{1} H_{2} trans_eq.
Suppose we have
a, b : nat
H_{1} : a = b
H_{2} : b = 42
trans_eq : ∀ (X : Type) (n m o : X),
n = m → m = o → n = o What is the type of this proof object?
trans_eq nat a b 42 H_{1} H_{2}
(1) a = b
(2) 42 = a
(3) a = 42
(4) Does not typecheck
a, b : nat
H_{1} : a = b
H_{2} : b = 42
trans_eq : ∀ (X : Type) (n m o : X),
n = m → m = o → n = o What is the type of this proof object?
trans_eq nat a b 42 H_{1} H_{2}
Check trans_eq nat a b 42 H_{1} H_{2}
: a = 42.
: a = 42.
Suppose, again, that we have
a, b : nat
H_{1} : a = b
H_{2} : b = 42
trans_eq : ∀ (X : Type) (n m o : X),
n = m → m = o → n = o What is the type of this proof object?
trans_eq nat b 42 a H_{2}
(1) b = a
(2) b = a → 42 = a
(3) 42 = a → b = a
(4) Does not typecheck
a, b : nat
H_{1} : a = b
H_{2} : b = 42
trans_eq : ∀ (X : Type) (n m o : X),
n = m → m = o → n = o What is the type of this proof object?
trans_eq nat b 42 a H_{2}
Check trans_eq nat b 42 a H_{2}
: 42 = a → b = a.
: 42 = a → b = a.
Suppose, again, that we have
a, b : nat
H_{1} : a = b
H_{2} : b = 42
trans_eq : ∀ (X : Type) (n m o : X),
n = m → m = o → n = o What is the type of this proof object?
trans_eq _ 42 a b
(1) a = b → b = 42 → a = 42
(2) 42 = a → a = b → 42 = b
(3) a = 42 → 42 = b → a = b
(4) Does not typecheck
a, b : nat
H_{1} : a = b
H_{2} : b = 42
trans_eq : ∀ (X : Type) (n m o : X),
n = m → m = o → n = o What is the type of this proof object?
trans_eq _ 42 a b
Check trans_eq _ 42 a b
: 42 = a → a = b → 42 = b.
: 42 = a → a = b → 42 = b.
Suppose, again, that we have
a, b : nat
H_{1} : a = b
H_{2} : b = 42
trans_eq : ∀ (X : Type) (n m o : X),
n = m → m = o → n = o What is the type of this proof object?
trans_eq _ _ _ _ H_{2} H_{1}
(1) b = a
(2) 42 = a
(3) a = 42
(4) Does not typecheck
a, b : nat
H_{1} : a = b
H_{2} : b = 42
trans_eq : ∀ (X : Type) (n m o : X),
n = m → m = o → n = o What is the type of this proof object?
trans_eq _ _ _ _ H_{2} H_{1}
Fail Check trans_eq _ _ _ _ H_{2} H_{1}.
Abort.
Coq vs. Set Theory
Functional Extensionality
Example function_equality_ex_{1} :
(fun x ⇒ 3 + x) = (fun x ⇒ (pred 4) + x).
(fun x ⇒ 3 + x) = (fun x ⇒ (pred 4) + x).
Proof. reflexivity. Qed.
In common mathematical practice, two functions f and g are
considered equal if they produce the same output on every input:
(∀ x, f x = g x) → f = g This is known as the principle of functional extensionality.
(∀ x, f x = g x) → f = g This is known as the principle of functional extensionality.
However, functional extensionality is not part of Coq's built-in logic. This means that some apparently "obvious" propositions are not provable.
Example function_equality_ex_{2} :
(fun x ⇒ plus x 1) = (fun x ⇒ plus 1 x).
Proof.
(* Stuck *)
Abort.
(fun x ⇒ plus x 1) = (fun x ⇒ plus 1 x).
Proof.
(* Stuck *)
Abort.
Axiom functional_extensionality : ∀ {X Y: Type}
{f g : X → Y},
(∀ (x:X), f x = g x) → f = g.
{f g : X → Y},
(∀ (x:X), f x = g x) → f = g.
Defining something as an 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:
Example function_equality_ex_{2} :
(fun x ⇒ plus x 1) = (fun x ⇒ plus 1 x).
Proof.
apply functional_extensionality. intros x.
apply add_comm.
Qed.
(fun x ⇒ plus x 1) = (fun x ⇒ plus 1 x).
Proof.
apply functional_extensionality. intros x.
apply add_comm.
Qed.
Naturally, we must be careful when adding new axioms into Coq's logic, as this can render it inconsistent -- that is, it may become possible to prove every proposition, including False, 2+2=5, etc.!
To check whether a particular proof relies on any additional axioms, use the Print Assumptions command: Print Assumptions function_equality_ex_{2}.
(* ===>
Axioms:
functional_extensionality :
forall (X Y : Type) (f g : X -> Y),
(forall x : X, f x = g x) -> f = g *)
Axioms:
functional_extensionality :
forall (X Y : Type) (f g : X -> Y),
(forall x : X, f x = g x) -> f = g *)
(If you try this yourself, you may also see add_comm listed as
an assumption, depending on whether the copy of Tactics.v in the
local directory has the proof of add_comm filled in.)
Is this provable by just reflexivity, without
functional_extensionality?
(fun xs ⇒ 1 :: xs) = (fun xs ⇒ [1] ++ xs)
(1) Yes
(2) No
Example cons_1_eq_ex : (fun xs ⇒ 1 :: xs) = (fun xs ⇒ [1] ++ xs).
Proof. reflexivity. Qed.
Proof. reflexivity. Qed.
Propositions vs. Booleans
We've seen two different ways of expressing logical claims in Coq: with booleans (of type bool), and with propositions (of type Prop).bool Prop ==== ==== decidable? yes no useable with match? yes no equalities rewritable? no yes
Working with Decidable Properties
- For example, even : nat → bool is a decision procedure for the property "is even".
- For example, the property "is the code of a halting Turing machine" is undecidable, so there is no way to write it as a function of type nat → bool.
- For example, "being the code of a halting Turing machine" is a perfectly legitimate mathematical property, and we can absolutely represent it as a Coq expression of type nat → Prop.
Example even_42_bool : even 42 = true.
Proof. reflexivity. Qed.
... or that there exists some k such that n = double k.
Example even_42_prop : Even 42.
Proof. unfold Even. ∃ 21. reflexivity. Qed.
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.
Lemma even_double : ∀ k, even (double k) = true.
Lemma even_double_conv : ∀ n, ∃ k,
n = if even n then double k else S (double k).
Proof.
intros k. induction k as [|k' IHk'].
- reflexivity.
- simpl. apply IHk'.
Qed.
intros k. induction k as [|k' IHk'].
- reflexivity.
- simpl. apply IHk'.
Qed.
Lemma even_double_conv : ∀ n, ∃ k,
n = if even n then double k else S (double k).
Proof.
(* Hint: Use the even_S lemma from Induction.v. *)
(* FILL IN HERE *) Admitted.
(* Hint: Use the even_S lemma from Induction.v. *)
(* FILL IN HERE *) Admitted.
☐
Theorem even_bool_prop : ∀ n,
even n = true ↔ Even n.
even n = true ↔ Even n.
Proof.
intros n. split.
- intros H. destruct (even_double_conv n) as [k Hk].
rewrite Hk. rewrite H. ∃ k. reflexivity.
- intros [k Hk]. rewrite Hk. apply even_double.
Qed.
intros n. split.
- intros H. destruct (even_double_conv n) as [k Hk].
rewrite Hk. rewrite H. ∃ k. reflexivity.
- intros [k Hk]. rewrite Hk. apply even_double.
Qed.
In view of this theorem, we say that the boolean computation
even 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.
Theorem eqb_eq : ∀ n_{1} n_{2} : nat,
n_{1} =? n_{2} = true ↔ n_{1} = n_{2}.
n_{1} =? n_{2} = true ↔ n_{1} = n_{2}.
Proof.
intros n_{1} n_{2}. split.
- apply eqb_true.
- intros H. rewrite H. rewrite eqb_refl. reflexivity.
Qed.
intros n_{1} n_{2}. split.
- apply eqb_true.
- intros H. rewrite H. rewrite eqb_refl. reflexivity.
Qed.
Even when the boolean and propositional formulations of a claim are equivalent from a purely logical perspective, they are often not equivalent from the point of view of convenience for some specific purpose.
For example, we cannot test whether or not a Prop is true in a function definition; as a consequence, the following code fragment is rejected:
Fail
Definition is_even_prime n :=
if n = 2 then true
else false.
Definition is_even_prime n :=
if n = 2 then true
else false.
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.
Example even_1000 : Even 1000.
The most direct way to prove this is to give the value of k
explicitly.
Proof. unfold Even. ∃ 500. reflexivity. Qed.
The proof of the corresponding boolean statement is even
simpler, because we don't have to invent the witness: Coq's
computation mechanism does it for us!
Example even_1000' : even 1000 = true.
Proof. reflexivity. Qed.
Proof. reflexivity. Qed.
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:
Example even_1000'' : Even 1000.
Proof. apply even_bool_prop. reflexivity. Qed.
Proof. apply even_bool_prop. reflexivity. Qed.
Example not_even_1001 : even 1001 = false.
Proof.
(* WORK IN CLASS *) Admitted.
Proof.
(* WORK IN CLASS *) Admitted.
In contrast, propositional negation can be more difficult
to work with directly.
Example not_even_1001' : ~(Even 1001).
Proof.
(* WORK IN CLASS *) Admitted.
Proof.
(* WORK IN CLASS *) Admitted.
Lemma plus_eqb_example : ∀ n m p : nat,
n =? m = true → n + p =? m + p = true.
Proof.
(* WORK IN CLASS *) Admitted.
n =? m = true → n + p =? m + p = true.
Proof.
(* WORK IN CLASS *) Admitted.
We won't discuss reflection any further for the moment, but it
serves as a good example showing the different strengths of
booleans and general propositions; being able to cross back and
forth between the boolean and propositional worlds will often be
convenient in later chapters.
The following reasoning principle is not derivable in
Coq (though, again, it can consistently be added):
Classical vs. Constructive Logic
Definition excluded_middle := ∀ P : Prop,
P ∨ ¬P.
P ∨ ¬P.
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.