Library Logic

Logic: Logic in Coq


Require Export MoreCoq.

Coq's built-in logic is very small: the only primitives are Inductive definitions, universal quantification (), and implication (), while all the other familiar logical connectives -- conjunction, disjunction, negation, existential quantification, even equality -- can be encoded using just these.
This chapter explains the encodings and shows how the tactics we've seen can be used to carry out standard forms of logical reasoning involving these connectives.

Propositions

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 Coq, the type of things that can (potentially) be proven is Prop.
Here is an example of a provable proposition:
Here is an example of an unprovable proposition:

Check ( (n:nat), n = 2).

Recall that Check asks Coq to tell us the type of the indicated expression.

Proofs and Evidence

In Coq, propositions have the same status as other types, such as nat. Just as the natural numbers 0, 1, 2, etc. inhabit the type nat, a Coq proposition P is inhabited by its proofs. We will refer to such inhabitants as proof term or proof object or evidence for the truth of P.
In Coq, when we state and then prove a lemma such as:
Lemma silly : 0 * 3 = 0. Proof. reflexivity. Qed.
the tactics we use within the Proof...Qed keywords tell Coq how to construct a proof term that inhabits the proposition. In this case, the proposition 0 × 3 = 0 is justified by a combination of the definition of mult, which says that 0 × 3 simplifies to just 0, and the reflexive principle of equality, which says that 0 = 0.

Lemma silly : 0 × 3 = 0.
Proof. reflexivity. Qed.

We can see which proof term Coq constructs for a given Lemma by using the Print directive:

Print silly.

Here, the eq_refl proof term witnesses the equality. (More on equality later!)

Implications are functions

Just as we can implement natural number multiplication as a function:
mult : nat nat nat
The proof term for an implication P Q is a function that takes evidence for P as input and produces evidence for Q as its output.
Lemma silly_implication : (1 + 1) = 2 → 0 × 3 = 0.
Proof. intros H. reflexivity. Qed.

We can see that the proof term for the above lemma is indeed a function:

Print silly_implication.

Defining Propositions

Just as we can create user-defined inductive types (like the lists, binary representations of natural numbers, etc., that we seen before), we can also create user-defined propositions.
Question: How do you define the meaning of a proposition?

The meaning of a proposition is given by rules and definitions that say how to construct evidence for the truth of the proposition from other evidence.
  • Typically, rules are defined inductively, just like any other datatype.
  • Sometimes a proposition is declared to be true without substantiating evidence. Such propositions are called axioms.
In this, and subsequence chapters, we'll see more about how these proof terms work in more detail.

Conjunction (Logical "and")

The logical conjunction of propositions P and Q can be represented using an Inductive definition with one constructor.
Inductive and (P Q : Prop) : Prop :=
  conj : PQ → (and P Q).

The intuition behind this definition is simple: to construct evidence for and P Q, we must provide evidence for P and evidence for Q. More precisely:
  • conj p q can be taken as evidence for and P Q if p is evidence for P and q is evidence for Q; and
  • this is the only way to give evidence for and P Q -- that is, if someone gives us evidence for and P Q, we know it must have the form conj p q, where p is evidence for P and q is evidence for Q.
Since we'll be using conjunction a lot, let's introduce a more familiar-looking infix notation for it.

Notation "P /\ Q" := (and P Q) : type_scope.

(The type_scope annotation tells Coq that this notation will be appearing in propositions, not values.)
Consider the "type" of the constructor conj:

Check conj.

Notice that it takes 4 inputs -- namely the propositions P and Q and evidence for P and Q -- and returns as output the evidence of P Q.

"Introducing" Conjuctions

Besides the elegance of building everything up from a tiny foundation, what's nice about defining conjunction this way is that we can prove statements involving conjunction using the tactics that we already know. For example, if the goal statement is a conjuction, we can prove it by applying the single constructor conj, which (as can be seen from the type of conj) solves the current goal and leaves the two parts of the conjunction as subgoals to be proved separately.
Theorem and_example :
  (0 = 0) (4 = mult 2 2).
Proof.
  apply conj.
  Case "left". reflexivity.
  Case "right". reflexivity. Qed.

Just for convenience, we can use the tactic split as a shorthand for apply conj.

Theorem and_example' :
  (0 = 0) (4 = mult 2 2).
Proof.
  split.
    Case "left". reflexivity.
    Case "right". reflexivity. Qed.

"Eliminating" conjunctions

Conversely, the inversion tactic can be used to take a conjunction hypothesis in the context, calculate what evidence must have been used to build it, and add variables representing this evidence to the proof context.

Theorem proj1 : P Q : Prop,
  P QP.
Proof.
  intros P Q H.
  inversion H as [HP HQ].
  apply HP. Qed.

Exercise: 1 star, optional (proj2)

Theorem proj2 : P Q : Prop,
  P QQ.
Proof.
intros P Q H. inversion H. apply H1. Qed.
Theorem and_commut : P Q : Prop,
  P QQ P.
Proof.
  intros P Q H.
  inversion H as [HP HQ].
  split.
    Case "left". apply HQ.
    Case "right". apply HP. Qed.

Exercise: 2 stars (and_assoc)

In the following proof, notice how the nested pattern in the inversion breaks the hypothesis H : P (Q R) down into HP: P, HQ : Q, and HR : R. Finish the proof from there:

Theorem and_assoc : P Q R : Prop,
  P (Q R)(P Q) R.
Proof.
  intros P Q R H.
  inversion H as [HP [HQ HR]].
  split.
  Case "left". inversion H. split.
    SCase "left". apply HP.
    SCase "right". apply HQ.
  Case "right". apply HR.
  Qed.

Iff

The handy "if and only if" connective is just the conjunction of two implications.
Definition iff (P Q : Prop) := (PQ) (QP).

Notation "P <-> Q" := (iff P Q)
                      (at level 95, no associativity)
                      : type_scope.

Theorem iff_implies : P Q : Prop,
  (P Q) → PQ.
Proof.
  intros P Q H.
  inversion H as [HAB HBA]. apply HAB. Qed.

Theorem iff_sym : P Q : Prop,
  (P Q) → (Q P).
Proof.
  intros P Q H.
  inversion H as [HAB HBA].
  split.
    Case "->". apply HBA.
    Case "<-". apply HAB. Qed.

Exercise: 1 star, 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.
  intros P. split.
    Case "->". intros. apply H.
    Case "<-". intros. apply H.
Qed.

Theorem iff_trans : P Q R : Prop,
  (P Q) → (Q R) → (P R).
Proof.
  intros P Q R H1 H2. split.
    Case "->". intros. apply H1 in H. apply H2 in H. apply H.
    Case "<-". intros. apply H2 in H. apply H1 in H. apply H.
 Qed.

Hint: If you have an iff hypothesis in the context, you can use inversion to break it into two separate implications. (Think about why this works.)
Some of Coq's tactics treat iff statements specially, thus avoiding the need for some low-level manipulation when reasoning with them. In particular, rewrite can be used with iff statements, not just equalities.

Disjunction (Logical "or")

Implementing Disjunction

Disjunction ("logical or") can also be defined as an inductive proposition.
Inductive or (P Q : Prop) : Prop :=
  | or_introl : Por P Q
  | or_intror : Qor P Q.

Notation "P \/ Q" := (or P Q) : type_scope.

Consider the "type" of the constructor or_introl:

Check or_introl.

It takes 3 inputs, namely the propositions P, Q and evidence of P, and returns, as output, the evidence of P Q. Next, look at the type of or_intror:

Check or_intror.

It is like or_introl but it requires evidence of Q instead of evidence of P.
Intuitively, there are two ways of giving evidence for P Q:
  • give evidence for P (and say that it is P you are giving evidence for -- this is the function of the or_introl constructor), or
  • give evidence for Q, tagged with the or_intror constructor.

Since P Q has two constructors, doing inversion on a hypothesis of type P Q yields two subgoals.
Theorem or_commut : P Q : Prop,
  P QQ P.
Proof.
  intros P Q H.
  inversion H as [HP | HQ].
    Case "left". apply or_intror. apply HP.
    Case "right". apply or_introl. apply HQ. Qed.

From here on, we'll use the shorthand tactics left and right in place of apply or_introl and apply or_intror.

Theorem or_commut' : P Q : Prop,
  P QQ P.
Proof.
  intros P Q H.
  inversion H as [HP | HQ].
    Case "left". right. apply HP.
    Case "right". left. apply HQ. Qed.

Theorem or_distributes_over_and_1 : P Q R : Prop,
  P (Q R)(P Q) (P R).
Proof.
  intros P Q R. intros H. inversion H as [HP | [HQ HR]].
    Case "left". split.
      SCase "left". left. apply HP.
      SCase "right". left. apply HP.
    Case "right". split.
      SCase "left". right. apply HQ.
      SCase "right". right. apply HR. Qed.

Exercise: 2 stars (or_distributes_over_and_2)

Theorem or_distributes_over_and_2 : P Q R : Prop,
  (P Q) (P R)P (Q R).
Proof.
  intros P Q R H.
  inversion H as [Hl Hr].
  inversion Hl as [Hll | Hlr].
  Case "P". left. apply Hll.
  Case "Q". inversion Hr as [Hrl | Hrr].
    SCase "P". left. apply Hrl.
    SCase "R". right. split. apply Hlr. apply Hrr.
Qed.

Exercise: 1 star, optional (or_distributes_over_and)

Theorem or_distributes_over_and : P Q R : Prop,
  P (Q R) (P Q) (P R).
Proof.
  intros P Q R.
  split. apply or_distributes_over_and_1. apply or_distributes_over_and_2.
Qed.

Relating and with andb and orb (advanced)

We've already seen several places where analogous structures can be found in Coq's computational (Type) and logical (Prop) worlds. Here is one more: the boolean operators andb and orb are clearly analogs of the logical connectives and . This analogy can be made more precise by the following theorems, which show how to translate knowledge about andb and orb's behaviors on certain inputs into propositional facts about those inputs.

Theorem andb_prop : b c,
  andb b c = trueb = true c = true.
Proof.
  intros b c H.
  destruct b.
    Case "b = true". destruct c.
      SCase "c = true". apply conj. reflexivity. reflexivity.
      SCase "c = false". inversion H.
    Case "b = false". inversion H. Qed.

Theorem andb_true_intro : b c,
  b = true c = trueandb b c = true.
Proof.
  intros b c H.
  inversion H.
  rewrite H0. rewrite H1. reflexivity. Qed.

Exercise: 2 stars, optional (bool_prop)

Theorem andb_false : b c,
  andb b c = falseb = false c = false.
Proof.
  intros b c H.
  destruct b. destruct c.
  Case "b = true, c = true". inversion H.
  Case "b = true, c = false". right. reflexivity.
  Case "b = false". left. reflexivity.
Qed.

Theorem orb_prop : b c,
  orb b c = trueb = true c = true.
Proof.
  intros b c H. destruct b.
  Case "b = true". left. reflexivity.
  Case "b = false". destruct c.
    SCase "c = true". right. reflexivity.
    SCase "c = false". inversion H.
Qed.

Theorem orb_false_elim : b c,
  orb b c = falseb = false c = false.
Proof.
  intros b c H. destruct b. destruct c.
  Case "b = true, c = true". inversion H.
  Case "b = true, c = false". inversion H.
  Case "b = false". destruct c. inversion H.
                    split. reflexivity. reflexivity.
Qed.

Falsehood

Logical falsehood can be represented in Coq as an inductively defined proposition with no constructors.
Inductive False : Prop := .

Intuition: False is a proposition for which there is no way to give evidence.
Since False has no constructors, inverting an assumption of type False always yields zero subgoals, allowing us to immediately prove any goal.

Theorem False_implies_nonsense :
  False → 2 + 2 = 5.
Proof.
  intros contra.
  inversion contra. Qed.

How does this work? The inversion tactic breaks contra into each of its possible cases, and yields a subgoal for each case. As contra is evidence for False, it has no possible cases, hence, there are no possible subgoals and the proof is done.

Conversely, the only way to prove False is if there is already something nonsensical or contradictory in the context:

Theorem nonsense_implies_False :
  2 + 2 = 5 → False.
Proof.
  intros contra.
  inversion contra. Qed.

Actually, since the proof of False_implies_nonsense doesn't actually have anything to do with the specific nonsensical thing being proved; it can easily be generalized to work for an arbitrary P:
Theorem ex_falso_quodlibet : (P:Prop),
  FalseP.
Proof.
  intros P contra.
  inversion contra. Qed.

The Latin ex falso quodlibet means, literally, "from falsehood follows whatever you please." This theorem is also known as the principle of explosion.

Truth

Since we have defined falsehood in Coq, one might wonder whether it is possible to define truth in the same way. We can.

Exercise: 2 stars, advanced (True)

Define True as another inductively defined proposition. (The intution is that True should be a proposition for which it is trivial to give evidence.)

However, unlike False, which we'll use extensively, True is used fairly rarely. By itself, it is trivial (and therefore uninteresting) to prove as a goal, and it carries no useful information as a hypothesis. But it can be useful when defining complex Props using conditionals, or as a parameter to higher-order Props.

Negation

The logical complement of a proposition P is written not P or, for shorthand, ¬P:
Definition not (P:Prop) := PFalse.

The intuition is that, if P is not true, then anything at all (even False) follows from assuming P.

Notation "~ x" := (not x) : type_scope.

Check not.

It takes a little practice to get used to working with negation in Coq. Even though you can see perfectly well why something is true, it can be a little hard at first to get things into the right configuration so that Coq can see it! Here are proofs of a few familiar facts about negation to get you warmed up.

Theorem not_False :
  ¬ False.
Proof.
  unfold not. intros H. inversion H. Qed.

Theorem contradiction_implies_anything : P Q : Prop,
  (P ¬P) → Q.
Proof.
  intros P Q H. inversion H as [HP HNA]. unfold not in HNA.
  apply HNA in HP. inversion HP. Qed.

Theorem double_neg : P : Prop,
  P~~P.
Proof.
  intros P H. unfold not. intros G. apply G. apply H. Qed.

Exercise: 2 stars, advanced (double_neg_inf)

Write an informal proof of double_neg:
Theorem: P implies ~~P, for any proposition P.
Proof: Assume P holds. By the definition of ~P it holds that ~~P = (P -> False) -> False We assume P and (P -> False) which yields False From lemma false_implies_nonsense it follows that False -> False holds.

Exercise: 2 stars (contrapositive)

Theorem contrapositive : P Q : Prop,
  (PQ) → (¬Q¬P).
Proof.
  intros P Q Himpl Hnot.
  unfold not. intros HP.
  apply Himpl in HP.
  apply contradiction_implies_anything with Q.
  split. apply HP. apply Hnot.
Qed.

Exercise: 1 star (not_both_true_and_false)

Theorem not_both_true_and_false : P : Prop,
  ¬ (P ¬P).
Proof.
  intro P. intros contr. apply contradiction_implies_anything with P. apply contr.
Qed.

Exercise: 1 star, advanced (informal_not_PNP)

Write an informal proof (in English) of the proposition P : Prop, ~(P ¬P).

Constructive logic

Note that some theorems that are true in classical logic are not provable in Coq's (constructive) logic. E.g., let's look at how this proof gets stuck...

Theorem classic_double_neg : P : Prop,
  ~~PP.
Proof.
  intros P H. unfold not in H.
  Abort.

Exercise: 5 stars, advanced, optional (classical_axioms)

For those who like a challenge, here is an exercise taken from the Coq'Art book (p. 123). The following five statements are often considered as characterizations of classical logic (as opposed to constructive logic, which is what is "built in" to Coq). We can't prove them in Coq, but we can consistently add any one of them as an unproven axiom if we wish to work in classical logic. Prove that these five propositions are equivalent.

Definition peirce := P Q: Prop,
  ((PQ)->P)->P.
Definition classic := P:Prop,
  ~~PP.
Definition excluded_middle := P:Prop,
  P ¬P.
Definition de_morgan_not_and_not := P Q:Prop,
  ~(~P ¬Q)PQ.
Definition implies_to_or := P Q:Prop,
  (PQ) → (¬PQ).

Exercise: 3 stars (excluded_middle_irrefutable)

This theorem implies that it is always safe to add a decidability axiom (i.e. an instance of excluded middle) for any particular Prop P. Why? Because we cannot prove the negation of such an axiom; if we could, we would have both ¬ (P ¬P) and ¬ ¬ (P ¬P), a contradiction.

Theorem excluded_middle_irrefutable: (P:Prop), ¬ ¬ (P ¬ P).
Proof.
Admitted.

Inequality

Saying x y is just the same as saying ~(x = y).

Notation "x <> y" := (¬ (x = y)) : type_scope.

Since inequality involves a negation, it again requires a little practice to be able to work with it fluently. Here is one very useful trick. If you are trying to prove a goal that is nonsensical (e.g., the goal state is false = true), apply the lemma ex_falso_quodlibet to change the goal to False. This makes it easier to use assumptions of the form ¬P that are available in the context -- in particular, assumptions of the form xy.
Theorem not_false_then_true : b : bool,
  b falseb = true.
Proof.
  intros b H. destruct b.
  Case "b = true". reflexivity.
  Case "b = false".
    unfold not in H.
    apply ex_falso_quodlibet.
    apply H. reflexivity. Qed.

Exercise: 2 stars (false_beq_nat)

Theorem false_beq_nat : n m : nat,
     n m
     beq_nat n m = false.
Proof.
Admitted.

Exercise: 2 stars, optional (beq_nat_false)

Theorem beq_nat_false : n m,
  beq_nat n m = falsen m.
Proof.
Admitted.