forked from oysteikt/sf1-template
1806 lines
60 KiB
Coq
1806 lines
60 KiB
Coq
(** * Logic: Logic in Rocq *)
|
|
|
|
Set Warnings "-notation-overridden".
|
|
Require Nat.
|
|
From LF Require Export Tactics.
|
|
|
|
(** We have now seen many examples of factual claims (i.e.,
|
|
_propositions_) and ways of presenting evidence of their truth
|
|
(_proofs_). In particular, we have worked extensively with
|
|
equality propositions ([e1 = e2]), implications ([P -> Q]), and
|
|
quantified propositions ([forall x, P]). In this chapter, we will
|
|
see how Rocq can be used to carry out other familiar forms of
|
|
logical reasoning.
|
|
|
|
Before diving into details, we should talk a bit about the status
|
|
of mathematical statements in Rocq. Rocq is a _typed_ language,
|
|
which means that every sensible expression has an associated type.
|
|
Logical claims are no exception: any statement we might try to
|
|
prove in Rocq has a type, namely [Prop], the type of
|
|
_propositions_. We can see this with the [Check] command: *)
|
|
|
|
Check (forall n m : nat, n + m = m + n) : Prop.
|
|
|
|
(** Note that _all_ syntactically well-formed propositions have type
|
|
[Prop] in Rocq, regardless of whether they are true or not.
|
|
|
|
Simply _being_ a proposition is one thing; being _provable_ is
|
|
a different thing! *)
|
|
|
|
Check 2 = 2 : Prop.
|
|
|
|
Check 3 = 2 : Prop.
|
|
|
|
Check forall n : nat, n = 2 : Prop.
|
|
|
|
(** Indeed, propositions don't just have types -- they are
|
|
_first-class_ entities that can be manipulated in all the same ways as
|
|
any of the other things in Rocq's world. *)
|
|
|
|
(** So far, we've seen one primary place where propositions can appear:
|
|
in [Theorem] (and [Lemma] and [Example]) declarations. *)
|
|
|
|
Theorem plus_2_2_is_4 :
|
|
2 + 2 = 4.
|
|
Proof. reflexivity. Qed.
|
|
|
|
(** But propositions can be used in other ways. For example, we
|
|
can give a name to a proposition using a [Definition], just as we
|
|
give names to other kinds of expressions. *)
|
|
|
|
Definition plus_claim : Prop := 2 + 2 = 4.
|
|
Check plus_claim : Prop.
|
|
|
|
(** We can later use this name in any situation where a proposition is
|
|
expected -- for example, as the claim in a [Theorem] declaration. *)
|
|
|
|
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. *)
|
|
|
|
(** 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.
|
|
Check is_three : nat -> Prop.
|
|
|
|
(** In Rocq, 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) : Prop :=
|
|
forall x y : A, f x = f y -> x = y.
|
|
|
|
Lemma succ_inj : injective S.
|
|
Proof.
|
|
intros x y H. injection H as H1. apply H1.
|
|
Qed.
|
|
|
|
(** The familiar equality operator [=] is a (binary) function that returns
|
|
a [Prop].
|
|
|
|
The expression [n = m] is syntactic sugar for [eq n m] (defined in
|
|
Rocq's standard library using the [Notation] mechanism).
|
|
|
|
Because [eq] can be used with elements of any type, it is also
|
|
polymorphic: *)
|
|
|
|
Check @eq : forall A : Type, A -> A -> Prop.
|
|
|
|
(** (Notice that we wrote [@eq] instead of [eq]: The type
|
|
argument [A] to [eq] is declared as implicit, and we need to turn
|
|
off the inference of this implicit argument to see the full type
|
|
of [eq].) *)
|
|
|
|
(* ################################################################# *)
|
|
(** * Logical Connectives *)
|
|
|
|
(* ================================================================= *)
|
|
(** ** Conjunction *)
|
|
|
|
(** The _conjunction_, or _logical and_, of propositions [A] and [B] is
|
|
written [A /\ B]; it represents the claim that both [A] and [B] are
|
|
true. *)
|
|
|
|
Example and_example : 3 + 4 = 7 /\ 2 * 2 = 4.
|
|
|
|
(** To prove a conjunction, start with the [split] tactic. This will
|
|
generate two subgoals, one for each part of the statement: *)
|
|
|
|
Proof.
|
|
split.
|
|
- (* 3 + 4 = 7 *) reflexivity.
|
|
- (* 2 * 2 = 4 *) reflexivity.
|
|
Qed.
|
|
|
|
(** For any propositions [A] and [B], if we assume that [A] and [B]
|
|
are each true individually, we can conclude that [A /\ B] is also
|
|
true. The Rocq library provides a function [conj] that does this. *)
|
|
|
|
Check @conj : forall A B : Prop, A -> B -> A /\ B.
|
|
|
|
(** 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 [conj] to achieve the same effect as
|
|
[split]. *)
|
|
|
|
Example and_example' : 3 + 4 = 7 /\ 2 * 2 = 4.
|
|
Proof.
|
|
apply conj.
|
|
- (* 3 + 4 = 7 *) reflexivity.
|
|
- (* 2 + 2 = 4 *) reflexivity.
|
|
Qed.
|
|
|
|
(** **** Exercise: 2 stars, standard (plus_is_O) *)
|
|
|
|
Example plus_is_O :
|
|
forall n m : nat, n + m = 0 -> n = 0 /\ m = 0.
|
|
Proof.
|
|
(* FILL IN HERE *) 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 can use our good old [destruct] tactic. *)
|
|
|
|
(** When the current proof context contains a hypothesis [H] of the
|
|
form [A /\ B], writing [destruct H as [HA HB]] will remove [H]
|
|
from the context and replace it with two new hypotheses: [HA],
|
|
stating that [A] is true, and [HB], stating that [B] is true. *)
|
|
|
|
Lemma and_example2 :
|
|
forall n m : nat, n = 0 /\ m = 0 -> n + m = 0.
|
|
Proof.
|
|
(* WORKED IN CLASS *)
|
|
intros n m H.
|
|
destruct H as [Hn Hm].
|
|
rewrite Hn. rewrite Hm.
|
|
reflexivity.
|
|
Qed.
|
|
|
|
(** As usual, we can also destruct [H] right at the point where we
|
|
introduce it, instead of introducing and then destructing it: *)
|
|
|
|
Lemma and_example2' :
|
|
forall 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 also have stated the
|
|
theorem with two separate premises: *)
|
|
|
|
Lemma and_example2'' :
|
|
forall n m : nat, n = 0 -> m = 0 -> n + m = 0.
|
|
Proof.
|
|
intros n m Hn Hm.
|
|
rewrite Hn. rewrite Hm.
|
|
reflexivity.
|
|
Qed.
|
|
|
|
(** For this specific 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 larger developments. Here's a
|
|
simple example: *)
|
|
|
|
Lemma and_example3 :
|
|
forall n m : nat, n + m = 0 -> n * m = 0.
|
|
Proof.
|
|
(* WORKED IN CLASS *)
|
|
intros n m H.
|
|
apply plus_is_O in H.
|
|
destruct H as [Hn Hm].
|
|
rewrite Hn. reflexivity.
|
|
Qed.
|
|
|
|
(** Another common situation is that we know [A /\ B] but in some
|
|
context we need just [A] or just [B]. In such cases we can do a
|
|
[destruct] (possibly implicitly, as part of an [intros]) and use
|
|
an underscore pattern [_] to indicate that the unneeded conjunct
|
|
should just be thrown away. *)
|
|
|
|
Lemma proj1 : forall P Q : Prop,
|
|
P /\ Q -> P.
|
|
Proof.
|
|
intros P Q HPQ.
|
|
destruct HPQ as [HP _].
|
|
apply HP. Qed.
|
|
|
|
(** **** Exercise: 1 star, standard, optional (proj2) *)
|
|
Lemma proj2 : forall P Q : Prop,
|
|
P /\ Q -> Q.
|
|
Proof.
|
|
(* FILL IN HERE *) Admitted.
|
|
(** [] *)
|
|
|
|
(** Finally, we sometimes need to rearrange the order of conjunctions
|
|
and/or the grouping of multi-way conjunctions. We can see this
|
|
at work in the proofs of the following commutativity and
|
|
associativity theorems *)
|
|
|
|
Theorem and_commut : forall P Q : Prop,
|
|
P /\ Q -> Q /\ P.
|
|
Proof.
|
|
intros P Q [HP HQ].
|
|
split.
|
|
- (* left *) apply HQ.
|
|
- (* right *) apply HP. Qed.
|
|
|
|
(** **** Exercise: 1 star, standard (and_assoc)
|
|
|
|
In the following proof of associativity, notice how the _nested_
|
|
[intros] pattern breaks the hypothesis [H : P /\ (Q /\ R)] down into
|
|
[HP : P], [HQ : Q], and [HR : R]. Finish the proof. *)
|
|
|
|
Theorem and_assoc : forall P Q R : Prop,
|
|
P /\ (Q /\ R) -> (P /\ Q) /\ R.
|
|
Proof.
|
|
intros P Q R [HP [HQ HR]].
|
|
(* FILL IN HERE *) Admitted.
|
|
(** [] *)
|
|
|
|
(** The infix notation [/\] is actually just syntactic sugar for
|
|
[and A B]. That is, [and] is a Rocq operator that takes two
|
|
propositions as arguments and yields a proposition. *)
|
|
|
|
Check and : Prop -> Prop -> Prop.
|
|
|
|
(* ================================================================= *)
|
|
(** ** Disjunction *)
|
|
|
|
(** 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 with other data types like [nat], can be done
|
|
explicitly with [destruct] or implicitly with an [intros]
|
|
pattern: *)
|
|
|
|
Lemma factor_is_O:
|
|
forall n m : nat, n = 0 \/ m = 0 -> n * m = 0.
|
|
Proof.
|
|
(* This intro 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 discharge
|
|
two proof obligations, each showing that the conclusion holds
|
|
under a different assumption -- [A] in the first subgoal and [B]
|
|
in the second.
|
|
|
|
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 the tactics
|
|
[left] and [right]. As their names imply, the first one requires
|
|
proving the left side of the disjunction, while the second
|
|
requires proving the right side. Here is a trivial use... *)
|
|
|
|
Lemma or_intro_l : forall A B : Prop, A -> A \/ B.
|
|
Proof.
|
|
intros A B HA.
|
|
left.
|
|
apply HA.
|
|
Qed.
|
|
|
|
(** ... and here is a slightly more interesting example requiring both
|
|
[left] and [right]: *)
|
|
|
|
Lemma zero_or_succ :
|
|
forall n : nat, n = 0 \/ n = S (pred n).
|
|
Proof.
|
|
(* WORKED IN CLASS *)
|
|
intros [|n'].
|
|
- left. reflexivity.
|
|
- right. reflexivity.
|
|
Qed.
|
|
|
|
(** **** Exercise: 2 stars, standard (mult_is_O) *)
|
|
Lemma mult_is_O :
|
|
forall n m, n * m = 0 -> n = 0 \/ m = 0.
|
|
Proof.
|
|
(* FILL IN HERE *) Admitted.
|
|
(** [] *)
|
|
|
|
(** **** Exercise: 1 star, standard (or_commut) *)
|
|
Theorem or_commut : forall P Q : Prop,
|
|
P \/ Q -> Q \/ P.
|
|
Proof.
|
|
(* FILL IN HERE *) Admitted.
|
|
(** [] *)
|
|
|
|
(* ================================================================= *)
|
|
(** ** Falsehood and Negation *)
|
|
|
|
(** Up to this point, we have mostly been concerned with proving
|
|
"positive" statements -- addition is commutative, appending lists
|
|
is associative, etc. We are sometimes also interested in negative
|
|
results, demonstrating that some 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.
|
|
|
|
Following this intuition, we could define [~ P] ("not [P]") as
|
|
[forall Q, P -> Q]. *)
|
|
|
|
(** Rocq actually makes an equivalent but slightly different choice,
|
|
defining [~ P] as [P -> False], where [False] is a specific
|
|
un-provable proposition defined in the standard library. *)
|
|
|
|
Module NotPlayground.
|
|
|
|
Definition not (P:Prop) := P -> False.
|
|
|
|
Check not : Prop -> Prop.
|
|
|
|
Notation "~ x" := (not x) : type_scope.
|
|
|
|
End NotPlayground.
|
|
|
|
(** Since [False] is a contradictory proposition, the principle of
|
|
explosion also applies to it. If we can get [False] into the context,
|
|
we can use [destruct] on it to complete any goal: *)
|
|
|
|
Theorem ex_falso_quodlibet : forall (P:Prop),
|
|
False -> P.
|
|
Proof.
|
|
intros P contra.
|
|
destruct contra. Qed.
|
|
|
|
(** 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)
|
|
|
|
Show that Rocq's definition of negation implies the intuitive one
|
|
mentioned above.
|
|
|
|
Hint: While getting accustomed to Rocq's definition of [not], you might
|
|
find it helpful to [unfold not] near the beginning of proofs. *)
|
|
|
|
Theorem not_implies_our_not : forall (P:Prop),
|
|
~ P -> (forall (Q:Prop), P -> Q).
|
|
Proof.
|
|
(* FILL IN HERE *) Admitted.
|
|
(** [] *)
|
|
|
|
(** Inequality is a very common form of negated statement, so there is a
|
|
special notation for it: *)
|
|
|
|
Notation "x <> y" := (~(x = y)) : type_scope.
|
|
|
|
(** For example: *)
|
|
|
|
Theorem zero_not_one : 0 <> 1.
|
|
Proof.
|
|
(** 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.) *)
|
|
unfold not.
|
|
(** To prove an inequality, we may assume the opposite
|
|
equality... *)
|
|
intros contra.
|
|
(** ... 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 Rocq.
|
|
Even though _you_ may see perfectly well why a claim involving
|
|
negation holds, it can be a little tricky at first to see how to make
|
|
Rocq understand it!
|
|
|
|
Here are proofs of a few familiar facts to help get you warmed up. *)
|
|
|
|
Theorem not_False :
|
|
~ False.
|
|
Proof.
|
|
unfold not. intros H. destruct H. Qed.
|
|
|
|
Theorem contradiction_implies_anything : forall P Q : Prop,
|
|
(P /\ ~P) -> Q.
|
|
Proof.
|
|
(* WORKED IN CLASS *)
|
|
intros P Q [HP HNP]. unfold not in HNP.
|
|
apply HNP in HP. destruct HP. Qed.
|
|
|
|
Theorem double_neg : forall P : Prop,
|
|
P -> ~~P.
|
|
Proof.
|
|
(* WORKED IN CLASS *)
|
|
intros P H. unfold not. intros G. apply G. apply H. Qed.
|
|
|
|
(** **** Exercise: 2 stars, advanced, optional (double_neg_informal)
|
|
|
|
Write an _informal_ proof of [double_neg]:
|
|
|
|
_Theorem_: [P] implies [~~P], for any proposition [P]. *)
|
|
|
|
(* FILL IN HERE *)
|
|
|
|
(* Do not modify the following line: *)
|
|
Definition manual_grade_for_double_neg_informal : option (nat*string) := None.
|
|
(** [] *)
|
|
|
|
(** **** Exercise: 1 star, standard, especially useful (contrapositive) *)
|
|
Theorem contrapositive : forall (P Q : Prop),
|
|
(P -> Q) -> (~Q -> ~P).
|
|
Proof.
|
|
(* FILL IN HERE *) Admitted.
|
|
(** [] *)
|
|
|
|
(** **** Exercise: 1 star, standard (not_both_true_and_false) *)
|
|
Theorem not_both_true_and_false : forall P : Prop,
|
|
~ (P /\ ~P).
|
|
Proof.
|
|
(* FILL IN HERE *) Admitted.
|
|
(** [] *)
|
|
|
|
(** **** Exercise: 1 star, advanced (not_PNP_informal)
|
|
|
|
Write an informal proof (in English) of the proposition [forall P
|
|
: Prop, ~(P /\ ~P)]. *)
|
|
|
|
(* FILL IN HERE *)
|
|
|
|
(* Do not modify the following line: *)
|
|
Definition manual_grade_for_not_PNP_informal : option (nat*string) := None.
|
|
(** [] *)
|
|
|
|
(** **** Exercise: 2 stars, standard (de_morgan_not_or)
|
|
|
|
_De Morgan's Laws_, named for Augustus De Morgan, describe how
|
|
negation interacts with conjunction and disjunction. The
|
|
following law says that "the negation of a disjunction is the
|
|
conjunction of the negations." There is a dual law
|
|
[de_morgan_not_and_not] to which we will return at the end of this
|
|
chapter. *)
|
|
Theorem de_morgan_not_or : forall (P Q : Prop),
|
|
~ (P \/ Q) -> ~P /\ ~Q.
|
|
Proof.
|
|
(* FILL IN HERE *) Admitted.
|
|
(** [] *)
|
|
|
|
(** **** Exercise: 1 star, standard, optional (not_S_inverse_pred)
|
|
|
|
Since we are working with natural numbers, we can disprove that
|
|
[S] and [pred] are inverses of each other: *)
|
|
Lemma not_S_pred_n : ~(forall n : nat, S (pred n) = n).
|
|
Proof.
|
|
(* FILL IN HERE *) 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.
|
|
|
|
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]. *)
|
|
|
|
Theorem not_true_is_false : forall 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.
|
|
|
|
(** Since reasoning with [ex_falso_quodlibet] is quite common, Rocq
|
|
provides a built-in tactic, [exfalso], for applying it. *)
|
|
|
|
Theorem not_true_is_false' : forall b : bool,
|
|
b <> true -> b = false.
|
|
Proof.
|
|
intros [] H. (* note implicit [destruct b] here! *)
|
|
- (* b = true *)
|
|
unfold not in H.
|
|
exfalso. (* <=== *)
|
|
apply H. reflexivity.
|
|
- (* b = false *) reflexivity.
|
|
Qed.
|
|
|
|
(* ================================================================= *)
|
|
(** ** Truth *)
|
|
|
|
(** Besides [False], Rocq's standard library also defines [True], a
|
|
proposition that is trivially true. To prove it, we use the
|
|
constant [I : True], which is also defined in the standard
|
|
library: *)
|
|
|
|
Lemma True_is_true : True.
|
|
Proof. apply I. Qed.
|
|
|
|
(** Unlike [False], which is used extensively, [True] is used
|
|
relatively rarely: it is trivial (and therefore uninteresting) to
|
|
prove as a goal, and it provides no useful information when it
|
|
appears as a hypothesis. *)
|
|
|
|
(** However, [True] can be quite useful when defining complex [Prop]s using
|
|
conditionals or as a parameter to higher-order [Prop]s. We'll come back
|
|
to this later.
|
|
|
|
For now, let's take a look at how we can use [True] and [False] to
|
|
achieve an effect similar to that of the [discriminate] tactic, without
|
|
literally using [discriminate]. *)
|
|
|
|
(** Pattern-matching lets us do different things for different
|
|
constructors. If the result of applying two different
|
|
constructors were hypothetically equal, then we could use [match]
|
|
to convert an unprovable statement (like [False]) to one that is
|
|
provable (like [True]). *)
|
|
|
|
Definition disc_fn (n: nat) : Prop :=
|
|
match n with
|
|
| O => True
|
|
| S _ => False
|
|
end.
|
|
|
|
Theorem disc_example : forall n, ~ (O = S n).
|
|
Proof.
|
|
intros n contra.
|
|
assert (H : disc_fn O). { simpl. apply I. }
|
|
rewrite contra in H. simpl in H. apply H.
|
|
Qed.
|
|
|
|
(** To generalize this to other constructors, we simply have to provide an
|
|
appropriate variant of [disc_fn]. To generalize it to other
|
|
conclusions, we can use [exfalso] to replace them with [False].
|
|
|
|
The built-in [discriminate] tactic takes care of all this for us. *)
|
|
|
|
(** **** Exercise: 2 stars, advanced, optional (nil_is_not_cons) *)
|
|
|
|
(** Use the same technique as above to show that [nil <> x :: xs].
|
|
Do not use the [discriminate] tactic. *)
|
|
|
|
Theorem nil_is_not_cons : forall X (x : X) (xs : list X), ~ (nil = x :: xs).
|
|
Proof.
|
|
(* FILL IN HERE *) Admitted.
|
|
(** [] *)
|
|
|
|
(* ================================================================= *)
|
|
(** ** Logical Equivalence *)
|
|
Print "<->".
|
|
(** The handy "if and only if" connective, which asserts that two
|
|
propositions have the same truth value, is simply the conjunction
|
|
of two implications.
|
|
|
|
Print "<->".
|
|
*)
|
|
(* ===>
|
|
Notation "A <-> B" := (iff A B)
|
|
|
|
iff = fun A B : Prop => (A -> B) /\ (B -> A)
|
|
: Prop -> Prop -> Prop
|
|
|
|
Argumments iff (A B)%type_scope *)
|
|
|
|
Theorem iff_sym : forall P Q : Prop,
|
|
(P <-> Q) -> (Q <-> P).
|
|
Proof.
|
|
(* WORKED IN CLASS *)
|
|
intros P Q [HAB HBA].
|
|
split.
|
|
- (* -> *) apply HBA.
|
|
- (* <- *) apply HAB. Qed.
|
|
|
|
Lemma not_true_iff_false : forall b,
|
|
b <> true <-> b = false.
|
|
Proof.
|
|
intros b. split.
|
|
- (* -> *) apply not_true_is_false.
|
|
- (* <- *)
|
|
intros H. rewrite H. intros H'. discriminate H'.
|
|
Qed.
|
|
|
|
(** We can also use [apply] with an [<->] in either direction,
|
|
without explicitly thinking about the fact that it is really an
|
|
[and] underneath. *)
|
|
|
|
Lemma apply_iff_example1:
|
|
forall P Q R : Prop, (P <-> Q) -> (Q -> R) -> (P -> R).
|
|
Proof.
|
|
intros P Q R Hiff H HP. apply H. apply Hiff. apply HP.
|
|
Qed.
|
|
|
|
Lemma apply_iff_example2:
|
|
forall P Q R : Prop, (P <-> Q) -> (P -> R) -> (Q -> R).
|
|
Proof.
|
|
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 : forall P : Prop,
|
|
P <-> P.
|
|
Proof.
|
|
(* FILL IN HERE *) Admitted.
|
|
|
|
Theorem iff_trans : forall P Q R : Prop,
|
|
(P <-> Q) -> (Q <-> R) -> (P <-> R).
|
|
Proof.
|
|
(* FILL IN HERE *) Admitted.
|
|
(** [] *)
|
|
|
|
(** **** Exercise: 3 stars, standard (or_distributes_over_and) *)
|
|
Theorem or_distributes_over_and : forall P Q R : Prop,
|
|
P \/ (Q /\ R) <-> (P \/ Q) /\ (P \/ R).
|
|
Proof.
|
|
(* FILL IN HERE *) Admitted.
|
|
(** [] *)
|
|
|
|
(* ================================================================= *)
|
|
(** ** Setoids and Logical Equivalence *)
|
|
|
|
(** Some of Rocq's tactics treat [iff] statements specially, avoiding 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 have to import the Rocq library that
|
|
supports it: *)
|
|
From Stdlib Require Import Setoids.Setoid.
|
|
|
|
(** A "setoid" is a set equipped with an equivalence relation -- that
|
|
is, a relation that is reflexive, symmetric, and transitive. When two
|
|
elements of a set are equivalent according to the relation, [rewrite]
|
|
can be used to replace one by the other.
|
|
|
|
We've seen this already with the equality relation [=] in Rocq: when
|
|
[x = y], we can use [rewrite] to replace [x] with [y] or vice-versa.
|
|
|
|
Similarly, the logical equivalence relation [<->] is reflexive,
|
|
symmetric, and transitive, so we can use it to replace one part of a
|
|
proposition with another: if [P <-> Q], then we can use [rewrite] to
|
|
replace [P] with [Q], or vice-versa. *)
|
|
|
|
(** Here is a simple example demonstrating how these tactics work with
|
|
[iff].
|
|
|
|
First, let's prove a couple of basic iff equivalences. (For these
|
|
proofs we are not using setoids yet.) *)
|
|
|
|
Lemma mul_eq_0 : forall n m, n * m = 0 <-> n = 0 \/ m = 0.
|
|
Proof.
|
|
split.
|
|
- apply mult_is_O.
|
|
- apply factor_is_O.
|
|
Qed.
|
|
|
|
Theorem or_assoc :
|
|
forall 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.
|
|
|
|
(** We can now use these facts with [rewrite] and [reflexivity] to
|
|
prove a ternary version of the [mult_eq_0] fact above _without_
|
|
splitting the top-level iff: *)
|
|
|
|
Lemma mul_eq_0_ternary :
|
|
forall 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 *)
|
|
|
|
(** Another fundamental 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 [exists x : T, P].
|
|
As with [forall], the type annotation [: T] can be omitted if Rocq
|
|
is able to infer from the context what the type of [x] should be. *)
|
|
|
|
(** To prove a statement of the form [exists x, P], we must show that [P]
|
|
holds for some specific choice for [x], known as the _witness_ of the
|
|
existential. This is done in two steps: First, we explicitly tell Rocq
|
|
which witness [t] we have in mind by invoking the tactic [exists t].
|
|
Then we prove that [P] holds after all occurrences of [x] are replaced
|
|
by [t]. *)
|
|
|
|
Definition Even x := exists n : nat, x = double n.
|
|
Check Even : nat -> Prop.
|
|
|
|
Lemma four_is_Even : Even 4.
|
|
Proof.
|
|
unfold Even. exists 2. reflexivity.
|
|
Qed.
|
|
|
|
(** Conversely, if we have an existential hypothesis [exists 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 : forall n,
|
|
(exists m, n = 4 + m) ->
|
|
(exists o, n = 2 + o).
|
|
Proof.
|
|
(* WORKED IN CLASS *)
|
|
intros n [m Hm]. (* note the implicit [destruct] here *)
|
|
exists (2 + m).
|
|
apply Hm. Qed.
|
|
|
|
(** **** Exercise: 1 star, standard, especially useful (dist_not_exists)
|
|
|
|
Prove that "[P] holds for all [x]" implies "there is no [x] for
|
|
which [P] does not hold." (Hint: [destruct H as [x E]] works on
|
|
existential assumptions!) *)
|
|
|
|
Theorem dist_not_exists : forall (X:Type) (P : X -> Prop),
|
|
(forall x, P x) -> ~ (exists x, ~ P x).
|
|
Proof.
|
|
(* FILL IN HERE *) Admitted.
|
|
(** [] *)
|
|
|
|
(** **** Exercise: 2 stars, standard (dist_exists_or)
|
|
|
|
Prove that existential quantification distributes over
|
|
disjunction. *)
|
|
|
|
Theorem dist_exists_or : forall (X:Type) (P Q : X -> Prop),
|
|
(exists x, P x \/ Q x) <-> (exists x, P x) \/ (exists x, Q x).
|
|
Proof.
|
|
(* FILL IN HERE *) Admitted.
|
|
(** [] *)
|
|
|
|
(** **** Exercise: 3 stars, standard, optional (leb_plus_exists) *)
|
|
Theorem leb_plus_exists : forall n m, n <=? m = true -> exists x, m = n+x.
|
|
Proof.
|
|
(* FILL IN HERE *) Admitted.
|
|
|
|
Theorem plus_exists_leb : forall n m, (exists x, m = n+x) -> n <=? m = true.
|
|
Proof.
|
|
(* FILL IN HERE *) Admitted.
|
|
|
|
(** [] *)
|
|
|
|
(* ################################################################# *)
|
|
(** * Programming with Propositions *)
|
|
|
|
(** 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 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 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. *)
|
|
|
|
Example In_example_1 : In 4 [1; 2; 3; 4; 5].
|
|
Proof.
|
|
(* WORKED IN CLASS *)
|
|
simpl. right. right. right. left. reflexivity.
|
|
Qed.
|
|
|
|
Example In_example_2 :
|
|
forall n, In n [2; 4] ->
|
|
exists n', n = 2 * n'.
|
|
Proof.
|
|
(* WORKED IN CLASS *)
|
|
simpl.
|
|
intros n [H | [H | []]].
|
|
- exists 1. rewrite <- H. reflexivity.
|
|
- exists 2. rewrite <- H. reflexivity.
|
|
Qed.
|
|
(** (Notice the use of the empty pattern to discharge the last case
|
|
_en passant_.) *)
|
|
|
|
(** We can also reason about more generic statements involving [In]. *)
|
|
|
|
Theorem In_map :
|
|
forall (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.
|
|
|
|
(** (Note here how [In] starts out applied to a variable and only
|
|
gets expanded when we do case analysis on this variable.) *)
|
|
|
|
(** This way of defining propositions recursively is very convenient in
|
|
some cases, less so in others. In particular, it is subject to Rocq's
|
|
usual restrictions regarding definitions 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 strengths and
|
|
limitations. *)
|
|
|
|
(** **** Exercise: 2 stars, standard (In_map_iff) *)
|
|
Theorem In_map_iff :
|
|
forall (A B : Type) (f : A -> B) (l : list A) (y : B),
|
|
In y (map f l) <->
|
|
exists x, f x = y /\ In x l.
|
|
Proof.
|
|
intros A B f l y. split.
|
|
- induction l as [|x l' IHl'].
|
|
(* FILL IN HERE *) Admitted.
|
|
(** [] *)
|
|
|
|
(** **** Exercise: 2 stars, standard (In_app_iff) *)
|
|
Theorem In_app_iff : forall A l l' (a:A),
|
|
In a (l++l') <-> In a l \/ In a l'.
|
|
Proof.
|
|
intros A l. induction l as [|a' l' IH].
|
|
(* FILL IN HERE *) Admitted.
|
|
(** [] *)
|
|
|
|
(** **** Exercise: 3 stars, standard, especially useful (All)
|
|
|
|
We noted above that functions returning propositions can be seen as
|
|
_properties_ of their arguments. For instance, if [P] has type
|
|
[nat -> Prop], then [P n] says that property [P] holds of [n].
|
|
|
|
Drawing inspiration from [In], write a recursive function [All]
|
|
stating that some property [P] holds of all elements of a list
|
|
[l]. To make sure your definition is correct, prove the [All_In]
|
|
lemma below. (Of course, your definition should _not_ just
|
|
restate the left-hand side of [All_In].) *)
|
|
|
|
Fixpoint All {T : Type} (P : T -> Prop) (l : list T) : Prop
|
|
(* REPLACE THIS LINE WITH ":= _your_definition_ ." *). Admitted.
|
|
|
|
Theorem All_In :
|
|
forall T (P : T -> Prop) (l : list T),
|
|
(forall x, In x l -> P x) <->
|
|
All P l.
|
|
Proof.
|
|
(* FILL IN HERE *) Admitted.
|
|
(** [] *)
|
|
|
|
(** **** Exercise: 2 stars, standard, optional (combine_odd_even)
|
|
|
|
Complete the definition of [combine_odd_even] below. It takes as
|
|
arguments two properties of numbers, [Podd] and [Peven], and it should
|
|
return a property [P] such that [P n] is equivalent to [Podd n] when
|
|
[n] is [odd] and equivalent to [Peven n] otherwise. *)
|
|
|
|
Definition combine_odd_even (Podd Peven : nat -> Prop) : nat -> Prop
|
|
(* REPLACE THIS LINE WITH ":= _your_definition_ ." *). Admitted.
|
|
|
|
(** To test your definition, prove the following facts: *)
|
|
|
|
Theorem combine_odd_even_intro :
|
|
forall (Podd Peven : nat -> Prop) (n : nat),
|
|
(odd n = true -> Podd n) ->
|
|
(odd n = false -> Peven n) ->
|
|
combine_odd_even Podd Peven n.
|
|
Proof.
|
|
(* FILL IN HERE *) Admitted.
|
|
|
|
Theorem combine_odd_even_elim_odd :
|
|
forall (Podd Peven : nat -> Prop) (n : nat),
|
|
combine_odd_even Podd Peven n ->
|
|
odd n = true ->
|
|
Podd n.
|
|
Proof.
|
|
(* FILL IN HERE *) Admitted.
|
|
|
|
Theorem combine_odd_even_elim_even :
|
|
forall (Podd Peven : nat -> Prop) (n : nat),
|
|
combine_odd_even Podd Peven n ->
|
|
odd n = false ->
|
|
Peven n.
|
|
Proof.
|
|
(* FILL IN HERE *) Admitted.
|
|
(** [] *)
|
|
|
|
(* ################################################################# *)
|
|
(** * Applying Theorems to Arguments *)
|
|
|
|
(** One feature that distinguishes Rocq 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 order to use Rocq. This section gives just a
|
|
taste, leaving a deeper exploration for the optional chapters
|
|
[ProofObjects] and [IndPrinciples]. *)
|
|
|
|
(** We have seen that we can use [Check] to ask Rocq to check whether
|
|
an expression has a given type: *)
|
|
|
|
Check plus : nat -> nat -> nat.
|
|
Check @rev : forall X, list X -> list X.
|
|
|
|
(** We can also use it to check what theorem a particular identifier
|
|
refers to: *)
|
|
|
|
Check add_comm : forall n m : nat, n + m = m + n.
|
|
Check plus_id_example : forall n m : nat, n = m -> n + n = m + m.
|
|
|
|
(** Rocq checks the _statements_ of the [add_comm] and
|
|
[plus_id_example] theorems in the same way that it checks the
|
|
_type_ of any term (e.g., plus). If we leave off the colon and
|
|
type, Rocq will print these types for us.
|
|
|
|
Why? *)
|
|
|
|
(** The reason is that the identifier [add_comm] actually refers to a
|
|
_proof object_ -- a logical derivation establishing the truth of the
|
|
statement [forall 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.
|
|
- If we have a term of type [nat -> nat -> nat], we can give it two
|
|
[nat]s as arguments and get a [nat] back.
|
|
|
|
Similarly, the statement of a theorem tells us what we can use that
|
|
theorem for.
|
|
- If we have a term of type [forall n m, n = m -> n + n = m + m] and we
|
|
provide it two numbers [n] and [m] and a third "argument" of type
|
|
[n = m], we get back a proof object of type [n + n = m + m]. *)
|
|
|
|
(** Operationally, this analogy goes even further: by applying a
|
|
theorem as if it were a function, i.e., applying it to values and
|
|
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: *)
|
|
|
|
Lemma add_comm3 :
|
|
forall 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 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.
|
|
|
|
(** We encountered similar issues back in [Induction], and we saw
|
|
one way to work around them by using [assert] to derive a specialized
|
|
version of [add_comm] that can be used to rewrite exactly where we
|
|
want. *)
|
|
|
|
Lemma add_comm3_take2 :
|
|
forall x y z, x + (y + z) = (z + y) + x.
|
|
Proof.
|
|
intros x y z.
|
|
rewrite add_comm.
|
|
assert (H : y + z = z + y).
|
|
{ rewrite add_comm. reflexivity. }
|
|
rewrite H.
|
|
reflexivity.
|
|
Qed.
|
|
|
|
(** A more elegant alternative is to apply [add_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. *)
|
|
|
|
Lemma add_comm3_take3 :
|
|
forall x y z, x + (y + z) = (z + y) + x.
|
|
Proof.
|
|
intros x y z.
|
|
rewrite add_comm.
|
|
rewrite (add_comm y z).
|
|
reflexivity.
|
|
Qed.
|
|
|
|
(** If we really wanted, we could in fact do it for both rewrites. *)
|
|
|
|
Lemma add_comm3_take4 :
|
|
forall x y z, x + (y + z) = (z + y) + x.
|
|
Proof.
|
|
intros x y z.
|
|
rewrite (add_comm x (y + z)).
|
|
rewrite (add_comm y z).
|
|
reflexivity.
|
|
Qed.
|
|
|
|
(** Here's another example of using a theorem about lists like
|
|
a function. Suppose we have proved the following simple fact
|
|
about lists... *)
|
|
|
|
Theorem in_not_nil :
|
|
forall 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.
|
|
|
|
(** (I.e., if a list [l] contains some element [x], then [l]
|
|
must be nonempty.) *)
|
|
|
|
(** Note that one quantified variable ([x]) does not appear in
|
|
the conclusion ([l <> []]). *)
|
|
|
|
(** Intuitively, we should be able to use this theorem to prove the special
|
|
case where [x] is [42]. However, simply invoking the tactic [apply
|
|
in_not_nil] will fail because it cannot infer the value of [x]. *)
|
|
|
|
Lemma in_not_nil_42 :
|
|
forall l : list nat, In 42 l -> l <> [].
|
|
Proof.
|
|
intros l H.
|
|
Fail apply in_not_nil.
|
|
Abort.
|
|
|
|
(** There are several ways to work around this: *)
|
|
|
|
(** We can use [apply ... with ...]: *)
|
|
Lemma in_not_nil_42_take2 :
|
|
forall l : list nat, In 42 l -> l <> [].
|
|
Proof.
|
|
intros l H.
|
|
apply in_not_nil with (x := 42).
|
|
apply H.
|
|
Qed.
|
|
|
|
(** Or we can use [apply ... in ...]: *)
|
|
Lemma in_not_nil_42_take3 :
|
|
forall l : list nat, In 42 l -> l <> [].
|
|
Proof.
|
|
intros l H.
|
|
apply in_not_nil in H.
|
|
apply H.
|
|
Qed.
|
|
|
|
(** Or -- this is the new one -- we can explicitly
|
|
apply the lemma to the value [42] for [x]: *)
|
|
Lemma in_not_nil_42_take4 :
|
|
forall l : list nat, In 42 l -> l <> [].
|
|
Proof.
|
|
intros l H.
|
|
apply (in_not_nil nat 42).
|
|
apply H.
|
|
Qed.
|
|
|
|
(** We can also explicitly apply the lemma to a hypothesis,
|
|
causing the values of the other parameters to be inferred: *)
|
|
Lemma in_not_nil_42_take5 :
|
|
forall l : list nat, In 42 l -> l <> [].
|
|
Proof.
|
|
intros l H.
|
|
apply (in_not_nil _ _ _ H).
|
|
Qed.
|
|
|
|
(** You can "use a theorem as a function" in this way with almost any
|
|
tactic that can take a theorem's 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 applying theorems to arguments.) *)
|
|
|
|
Example lemma_application_ex :
|
|
forall {n : nat} {ns : list nat},
|
|
In n (map (fun m => m * 0) ns) ->
|
|
n = 0.
|
|
Proof.
|
|
intros n ns H.
|
|
destruct (proj1 _ _ (In_map_iff _ _ _ _ _) H)
|
|
as [m [Hm _]].
|
|
rewrite mul_0_r in Hm. rewrite <- Hm. reflexivity.
|
|
Qed.
|
|
|
|
(** We will see many more examples in later chapters. *)
|
|
|
|
(* ################################################################# *)
|
|
(** * Working with Decidable Properties *)
|
|
|
|
(** We've seen two different ways of expressing logical claims in Rocq:
|
|
with _booleans_ (of type [bool]), and with _propositions_ (of type
|
|
[Prop]).
|
|
|
|
Here are the key differences between [bool] and [Prop]:
|
|
|
|
bool Prop
|
|
==== ====
|
|
decidable? yes no
|
|
useable with match? yes no
|
|
works with rewrite tactic? no yes
|
|
*)
|
|
|
|
(** The crucial difference between the two worlds is _decidability_.
|
|
Every (closed) Rocq expression of type [bool] can be simplified in a
|
|
finite number of steps to either [true] or [false] -- i.e., there is a
|
|
terminating mechanical procedure for deciding whether or not it is
|
|
[true].
|
|
|
|
This means that, for example, the type [nat -> bool] is inhabited only
|
|
by functions that, given a [nat], always yield either [true] or [false]
|
|
in finite time; and this, in turn, means (by a standard computability
|
|
argument) that there is _no_ function in [nat -> bool] that checks
|
|
whether a given number is the code of a terminating Turing machine.
|
|
|
|
By contrast, the type [Prop] includes both decidable and undecidable
|
|
mathematical propositions; in particular, the type [nat -> Prop] does
|
|
contain functions representing properties like "the nth Turing machine
|
|
halts."
|
|
|
|
The second row in the table follows directly from this essential
|
|
difference. To evaluate a pattern match (or conditional) on a boolean,
|
|
we need to know whether the scrutinee evaluates to [true] or [false];
|
|
this only works for [bool], not [Prop].
|
|
|
|
The third row highlights an important practical difference:
|
|
equality functions like [eqb_nat] that return a boolean cannot be
|
|
used directly to justify rewriting with the [rewrite] tactic;
|
|
propositional equality is required for this. *)
|
|
|
|
|
|
(** Since [Prop] includes _both_ decidable and undecidable properties, we
|
|
have two options when we want to formalize a property that happens to
|
|
be decidable: we can express it either as a boolean computation or as a
|
|
function into [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. exists 21. reflexivity. Qed.
|
|
|
|
(** Of course, it would be deeply strange if these two
|
|
characterizations of evenness did not describe the same set of
|
|
natural numbers!
|
|
|
|
Fortunately, they do! *)
|
|
|
|
(** To prove this, we first need two helper lemmas. *)
|
|
|
|
Lemma even_double : forall k, even (double k) = true.
|
|
Proof.
|
|
intros k. induction k as [|k' IHk'].
|
|
- reflexivity.
|
|
- simpl. apply IHk'.
|
|
Qed.
|
|
|
|
(** **** Exercise: 3 stars, standard (even_double_conv) *)
|
|
Lemma even_double_conv : forall n, exists 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.
|
|
(** [] *)
|
|
|
|
(** Now the main theorem: *)
|
|
|
|
Theorem even_bool_prop : forall n,
|
|
even n = true <-> Even n.
|
|
Proof.
|
|
intros n. split.
|
|
- intros H. destruct (even_double_conv n) as [k Hk].
|
|
rewrite Hk. rewrite H. exists k. reflexivity.
|
|
- intros [k Hk]. rewrite Hk. apply even_double.
|
|
Qed.
|
|
|
|
(** In view of this theorem, we can say that the boolean computation
|
|
[even n] is _reflected_ in the truth of the proposition
|
|
[exists 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].
|
|
Again, these two notions are equivalent: *)
|
|
|
|
Theorem eqb_eq : forall n1 n2 : nat,
|
|
n1 =? n2 = true <-> n1 = n2.
|
|
Proof.
|
|
intros n1 n2. split.
|
|
- apply eqb_true.
|
|
- intros H. rewrite H. rewrite eqb_refl. reflexivity.
|
|
Qed.
|
|
|
|
(** So what should we do in situations where some claim could be
|
|
formalized as either a proposition or a boolean computation? Which
|
|
should we choose?
|
|
|
|
In general, _both_ can be useful. *)
|
|
|
|
(** For example, booleans are more useful for defining functions.
|
|
There is no effective way to _test_ whether or not a [Prop] is
|
|
true, so we cannot use [Prop]s in conditional expressions. The
|
|
following definition is rejected: *)
|
|
|
|
Fail
|
|
Definition is_even_prime n :=
|
|
if n = 2 then true
|
|
else false.
|
|
|
|
(** Rocq complains that [n = 2] has type [Prop], while it expects an
|
|
element of [bool] (or some other inductive type with two constructors).
|
|
This has to do with the _computational_ nature of Rocq's core language,
|
|
which is designed so that every function it can express is computable
|
|
and total. (One reason for this is to allow the extraction of
|
|
executable programs from Rocq developments.) As a consequence, [Prop] in
|
|
Rocq 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. *)
|
|
|
|
(** Rather, we have to state this definition using a boolean equality
|
|
test. *)
|
|
|
|
Definition is_even_prime n :=
|
|
if n =? 2 then true
|
|
else false.
|
|
|
|
(** Beyond the fact that non-computable properties are impossible in
|
|
general to phrase as boolean computations, even many _computable_
|
|
properties are easier to express using [Prop] than [bool], since
|
|
recursive function definitions in Rocq are subject to significant
|
|
restrictions. 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
|
|
matching algorithm, which would be more complicated, harder to
|
|
understand, and harder to reason about than a simple (non-algorithmic)
|
|
definition of this property.
|
|
|
|
Conversely, an important side benefit of stating facts using booleans
|
|
is enabling some proof automation through computation with Rocq terms, a
|
|
technique known as _proof by reflection_.
|
|
|
|
Consider the following statement: *)
|
|
|
|
Example even_1000 : Even 1000.
|
|
|
|
(** The most direct way to prove this is to give the value of [k]
|
|
explicitly. *)
|
|
|
|
Proof. unfold Even. exists 500. reflexivity. Qed.
|
|
|
|
(** The proof of the corresponding boolean statement is simpler, because we
|
|
don't have to invent the witness [500]: Rocq's computation mechanism
|
|
does it for us! *)
|
|
|
|
Example even_1000' : even 1000 = true.
|
|
Proof. reflexivity. Qed.
|
|
|
|
(** Now, the useful observation 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.
|
|
|
|
(** Although we haven't gained much in terms of proof-script
|
|
line count in this case, larger proofs can often be made considerably
|
|
simpler by the use of reflection. As an extreme example, a famous
|
|
Rocq proof of the even more famous _4-color theorem_ uses
|
|
reflection to reduce the analysis of hundreds of different cases
|
|
to a boolean computation. *)
|
|
|
|
(** Another advantage of booleans is that the _negation_ of a claim
|
|
about booleans is straightforward to state and (when true) prove:
|
|
simply flip the expected boolean result. *)
|
|
|
|
Example not_even_1001 : even 1001 = false.
|
|
Proof.
|
|
reflexivity.
|
|
Qed.
|
|
|
|
(** In contrast, propositional negation can be difficult to work with
|
|
directly.
|
|
|
|
For example, suppose we state the non-evenness of [1001]
|
|
propositionally: *)
|
|
|
|
Example not_even_1001' : ~(Even 1001).
|
|
|
|
(** Proving this directly -- by assuming that there is some [n] such that
|
|
[1001 = double n] and then somehow reasoning to a contradiction --
|
|
would be rather complicated.
|
|
|
|
But if we convert it to a claim about the boolean [even] function, we
|
|
can let Rocq do the work for us. *)
|
|
|
|
Proof.
|
|
(* WORKED IN CLASS *)
|
|
rewrite <- even_bool_prop.
|
|
unfold not.
|
|
simpl.
|
|
intro H.
|
|
discriminate H.
|
|
Qed.
|
|
|
|
(** Conversely, there are situations where it can be easier to work
|
|
with propositions rather than booleans.
|
|
|
|
In particular, knowing that [(n =? m) = true] is generally of
|
|
little direct help in the middle of a proof involving [n] and [m].
|
|
But if we convert the statement to the equivalent form [n = m],
|
|
then we can easily [rewrite] with it. *)
|
|
|
|
Lemma plus_eqb_example : forall n m p : nat,
|
|
n =? m = true -> n + p =? m + p = true.
|
|
Proof.
|
|
(* WORKED IN CLASS *)
|
|
intros n m p H.
|
|
rewrite eqb_eq in H.
|
|
rewrite H.
|
|
rewrite eqb_eq.
|
|
reflexivity.
|
|
Qed.
|
|
|
|
(** 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. *)
|
|
|
|
(** **** Exercise: 2 stars, standard (logical_connectives)
|
|
|
|
The following theorems relate the propositional connectives studied
|
|
in this chapter to the corresponding boolean operations. *)
|
|
|
|
Theorem andb_true_iff : forall b1 b2:bool,
|
|
b1 && b2 = true <-> b1 = true /\ b2 = true.
|
|
Proof.
|
|
(* FILL IN HERE *) Admitted.
|
|
|
|
Theorem orb_true_iff : forall b1 b2,
|
|
b1 || b2 = true <-> b1 = true \/ b2 = true.
|
|
Proof.
|
|
(* FILL IN HERE *) Admitted.
|
|
(** [] *)
|
|
|
|
(** **** Exercise: 1 star, standard (eqb_neq)
|
|
|
|
The following theorem is an alternate "negative" formulation of
|
|
[eqb_eq] that is more convenient in certain situations. (We'll see
|
|
examples in later chapters.) Hint: [not_true_iff_false]. *)
|
|
|
|
Theorem eqb_neq : forall x y : nat,
|
|
x =? y = false <-> x <> y.
|
|
Proof.
|
|
(* FILL IN HERE *) Admitted.
|
|
(** [] *)
|
|
|
|
(** **** Exercise: 3 stars, standard (eqb_list)
|
|
|
|
Given a boolean operator [eqb] for testing equality of elements of
|
|
some type [A], we can define a function [eqb_list] for testing
|
|
equality of lists with elements in [A]. Complete the definition
|
|
of the [eqb_list] function below. To make sure that your
|
|
definition is correct, prove the lemma [eqb_list_true_iff]. *)
|
|
|
|
Fixpoint eqb_list {A : Type} (eqb : A -> A -> bool)
|
|
(l1 l2 : list A) : bool
|
|
(* REPLACE THIS LINE WITH ":= _your_definition_ ." *). Admitted.
|
|
|
|
Theorem eqb_list_true_iff :
|
|
forall A (eqb : A -> A -> bool),
|
|
(forall a1 a2, eqb a1 a2 = true <-> a1 = a2) ->
|
|
forall l1 l2, eqb_list eqb l1 l2 = true <-> l1 = l2.
|
|
Proof.
|
|
(* FILL IN HERE *) Admitted.
|
|
|
|
(** [] *)
|
|
|
|
(** **** Exercise: 2 stars, standard, especially useful (All_forallb)
|
|
|
|
Prove the theorem below, which relates [forallb], from the
|
|
exercise [forall_exists_challenge] in chapter [Tactics], to
|
|
the [All] property defined above. *)
|
|
|
|
(** Copy the definition of [forallb] from your [Tactics] here
|
|
so that this file can be graded on its own. *)
|
|
Fixpoint forallb {X : Type} (test : X -> bool) (l : list X) : bool
|
|
(* REPLACE THIS LINE WITH ":= _your_definition_ ." *). Admitted.
|
|
|
|
Theorem forallb_true_iff : forall X test (l : list X),
|
|
forallb test l = true <-> All (fun x => test x = true) l.
|
|
Proof.
|
|
(* FILL IN HERE *) Admitted.
|
|
|
|
(** (Ungraded thought question) Are there any important properties of
|
|
the function [forallb] which are not captured by this
|
|
specification? *)
|
|
|
|
(* FILL IN HERE
|
|
|
|
[] *)
|
|
|
|
(* ################################################################# *)
|
|
(** * The Logic of Rocq *)
|
|
|
|
(** Rocq'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 definitions and proofs -- in particular from
|
|
Zermelo-Fraenkel Set Theory (ZFC), the most popular foundation for
|
|
paper-and-pencil mathematics.
|
|
|
|
We conclude this chapter with a brief discussion of some of the
|
|
most significant differences between these two worlds. *)
|
|
|
|
(* ================================================================= *)
|
|
(** ** Functional Extensionality *)
|
|
|
|
(** Rocq's logic is quite minimalistic. This means that one occasionally
|
|
encounters cases where translating standard mathematical reasoning into
|
|
Rocq is cumbersome -- or even impossible -- unless we enrich its core
|
|
logic with additional axioms. *)
|
|
|
|
(** For example, the equality assertions that we have seen so far
|
|
mostly have concerned elements of inductive types ([nat], [bool],
|
|
etc.). But, since Rocq's equality operator is polymorphic, we can use
|
|
it at _any_ type -- in particular, we can write propositions claiming
|
|
that two _functions_ are equal to each other:
|
|
|
|
In certain cases Rocq can successfully prove equality propositions stating
|
|
that two _functions_ are equal to each other: **)
|
|
|
|
Example function_equality_ex1 :
|
|
(fun x => 3 + x) = (fun x => (pred 4) + x).
|
|
Proof. reflexivity. Qed.
|
|
|
|
(** This works when Rocq can simplify the functions to the same expression,
|
|
but this doesn't always happen. **)
|
|
|
|
(** These two functions are equal just by simplification, but in general
|
|
functions can be equal for more interesting reasons.
|
|
|
|
In common mathematical practice, two functions [f] and [g] are
|
|
considered equal if they produce the same output on every input:
|
|
|
|
(forall x, f x = g x) -> f = g
|
|
|
|
This is known as the principle of _functional extensionality_. *)
|
|
|
|
(** (Informally, 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., the results we obtain
|
|
after applying it.) *)
|
|
|
|
(** However, functional extensionality is not part of Rocq's built-in logic.
|
|
This means that some intuitively obvious propositions are not
|
|
provable. *)
|
|
|
|
Example function_equality_ex2 :
|
|
(fun x => plus x 1) = (fun x => plus 1 x).
|
|
Proof.
|
|
Fail reflexivity. Fail rewrite add_comm.
|
|
(* Stuck *)
|
|
Abort.
|
|
|
|
(** However, if we like, we can add functional extensionality to Rocq
|
|
using the [Axiom] command. *)
|
|
|
|
Axiom functional_extensionality : forall {X Y: Type}
|
|
{f g : X -> Y},
|
|
(forall (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_ex2 :
|
|
(fun x => plus x 1) = (fun x => plus 1 x).
|
|
Proof.
|
|
apply functional_extensionality. intros x.
|
|
apply add_comm.
|
|
Qed.
|
|
|
|
(** Naturally, we need to be quite careful when adding new axioms into
|
|
Rocq's logic, as this can render it _inconsistent_ -- that is, it may
|
|
become possible to prove every proposition, including [False], [2+2=5],
|
|
etc.!
|
|
|
|
In general, there is no simple way of telling whether an axiom is safe
|
|
to add: hard work by highly trained mathematicians is often 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:
|
|
|
|
Print Assumptions function_equality_ex2.
|
|
*)
|
|
(* ===>
|
|
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.) *)
|
|
|
|
(** **** Exercise: 4 stars, standard (tr_rev_correct)
|
|
|
|
One problem with the definition of the list-reversing function [rev]
|
|
that we have is that it performs a call to [app] on each step. Running
|
|
[app] takes time asymptotically linear in the size of the list, which
|
|
means that [rev] is asymptotically quadratic.
|
|
|
|
We can improve this with the following two-argument definition: *)
|
|
|
|
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 of [rev] 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. *)
|
|
|
|
Theorem tr_rev_correct : forall X, @tr_rev X = @rev X.
|
|
Proof.
|
|
(* FILL IN HERE *) Admitted.
|
|
(** [] *)
|
|
|
|
(* ================================================================= *)
|
|
(** ** Classical vs. Constructive Logic *)
|
|
|
|
(** We have seen that it is not possible to test whether or not a
|
|
proposition [P] holds while defining a Rocq function. You may be
|
|
surprised to learn that a similar restriction applies in _proofs_!
|
|
In other words, the following intuitive reasoning principle is not
|
|
derivable in Rocq: *)
|
|
|
|
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. *)
|
|
|
|
(** However, in the special case where we happen to know that [P] is
|
|
reflected in some boolean term [b], knowing whether it holds or
|
|
not is trivial: we just have to check the value of [b]. *)
|
|
|
|
Theorem restricted_excluded_middle : forall P b,
|
|
(P <-> b = true) -> P \/ ~ P.
|
|
Proof.
|
|
intros P [] H.
|
|
- left. rewrite H. reflexivity.
|
|
- right. rewrite H. intros contra. discriminate contra.
|
|
Qed.
|
|
|
|
(** In particular, the excluded middle is valid for equations [n = m],
|
|
between natural numbers [n] and [m]. *)
|
|
|
|
Theorem restricted_excluded_middle_eq : forall (n m : nat),
|
|
n = m \/ n <> m.
|
|
Proof.
|
|
intros n m.
|
|
apply (restricted_excluded_middle (n = m) (n =? m)).
|
|
symmetry.
|
|
apply eqb_eq.
|
|
Qed.
|
|
|
|
(** Sadly, this trick only works for decidable propositions. *)
|
|
|
|
(** It may seem strange that the general excluded middle is not
|
|
available by default in Rocq, since it is a standard feature of familiar
|
|
logics like ZFC. But there is a distinct advantage in _not_ assuming
|
|
the excluded middle: statements in Rocq make stronger claims than the
|
|
analogous statements in standard mathematics. Notably, a Rocq proof of
|
|
[exists x, P x] always includes a particular value of [x] for which we
|
|
can prove [P x] -- in other words, every proof of existence is
|
|
_constructive_. *)
|
|
|
|
(** Logics like Rocq's, which do not assume the excluded middle, are
|
|
referred to as _constructive logics_.
|
|
|
|
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] ([a] to the power [b]) is rational.
|
|
|
|
_Proof_: It is not difficult to show that [sqrt 2] is irrational. So 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 this, we finish the proof knowing that such [a] and [b]
|
|
exist, but not being sure of their actual values.
|
|
|
|
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 only 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 Rocq's logic,
|
|
allowing us to add it safely as an axiom. However, we will not
|
|
need to do so here: the results that we cover in Software
|
|
Foundations 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, [~ exists x, P x]. From this premise, it is not
|
|
hard to derive [forall x, ~ P x]. If we manage to show that this
|
|
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 [exists x, P x] using a proof of [~ ~ (exists x, P x)].
|
|
Allowing ourselves to remove double negations from arbitrary
|
|
statements is equivalent to assuming the excluded middle law, as shown
|
|
in one of the exercises below. Thus, this line of reasoning cannot be
|
|
encoded in Rocq without assuming additional axioms. *)
|
|
|
|
(** **** Exercise: 3 stars, standard (excluded_middle_irrefutable)
|
|
|
|
Proving the consistency of Rocq with the general excluded middle
|
|
axiom requires complicated reasoning that cannot be carried out
|
|
within Rocq itself. However, the following theorem implies that it
|
|
is always safe to assume a decidability axiom (i.e., an instance
|
|
of excluded middle) for any _particular_ Prop [P]. Why? Because
|
|
the negation of such an axiom leads to a contradiction. If [~ (P
|
|
\/ ~P)] were provable, then by [de_morgan_not_or] as proved above,
|
|
[P /\ ~P] would be provable, which would be a contradiction. So, it
|
|
is safe to add [P \/ ~P] as an axiom for any particular [P].
|
|
|
|
Succinctly: for any proposition P,
|
|
[Rocq is consistent ==> Rocq + (P \/ ~P) is consistent]. *)
|
|
|
|
Theorem excluded_middle_irrefutable: forall (P : Prop),
|
|
~ ~ (P \/ ~ P).
|
|
Proof.
|
|
(* FILL IN HERE *) Admitted.
|
|
(** [] *)
|
|
|
|
(** **** Exercise: 3 stars, advanced (not_exists_dist)
|
|
|
|
It is a theorem of classical logic that the following two
|
|
assertions are equivalent:
|
|
|
|
~ (exists x, ~ P x)
|
|
forall x, P x
|
|
|
|
The [dist_not_exists] theorem above proves one side of this
|
|
equivalence. Interestingly, the other direction cannot be proved
|
|
in constructive logic. Your job is to show that it is implied by
|
|
the excluded middle. *)
|
|
|
|
Theorem not_exists_dist :
|
|
excluded_middle ->
|
|
forall (X:Type) (P : X -> Prop),
|
|
~ (exists x, ~ P x) -> (forall x, P x).
|
|
Proof.
|
|
(* FILL IN HERE *) Admitted.
|
|
(** [] *)
|
|
|
|
(** **** Exercise: 5 stars, standard, optional (classical_axioms)
|
|
|
|
For those who like a challenge, here is an exercise adapted from the
|
|
Coq'Art book by Bertot and Casteran (p. 123). Each of the
|
|
following five statements, together with [excluded_middle], can be
|
|
considered as characterizing classical logic. We can't prove any
|
|
of them in Rocq, but we can consistently add any _one_ of them as an
|
|
axiom if we wish to work in classical logic.
|
|
|
|
To see this, prove that all six propositions (these five plus
|
|
[excluded_middle]) are equivalent.
|
|
|
|
Hint: Rather than considering all pairs of statements pairwise,
|
|
prove a single circular chain of implications that connects them
|
|
all. *)
|
|
|
|
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).
|
|
|
|
Definition consequentia_mirabilis := forall P:Prop,
|
|
(~P -> P) -> P.
|
|
|
|
(* FILL IN HERE
|
|
|
|
[] *)
|
|
|
|
(* 2026-01-07 13:17 *)
|