(* Formalizing and Proving Theorems in Coq --- Homework 6
curated by Tobias KappĂ©, May 2022.
For these exercises, proceed as before: find the lemmas marked as homework,
and solve them using the techniques we discussed in class. *)
(* We use our earlier formalization of "less than or equal". *)
Inductive less_than_equal: nat -> nat -> Prop :=
| LERefl: forall (n: nat), n <= n
| LESucc: forall (n m: nat), n <= m -> n <= S m
where "n <= m" := (less_than_equal n m).
(* Let's prove that zero is less than or equal to two. *)
Lemma zero_less_than_two: 0 <= 2.
Proof.
apply LESucc.
apply LESucc.
apply LERefl.
Qed.
(* If proofs are programs, what is this program? *)
Print zero_less_than_two.
(* Let's do that again, and show the intermediate programs. *)
Lemma zero_less_than_two': 0 <= 2.
Proof.
Show Proof.
apply LESucc.
Show Proof.
apply LESucc.
Show Proof.
apply LERefl.
Qed.
(* Okay, can we do that in one go? It turns out we can! *)
Lemma zero_less_than_two'': 0 <= 2.
Proof.
apply (LESucc 0 1 (LESucc 0 0 (LERefl 0))).
Qed.
(* Alternatively, we can just *define* our proof like this. *)
Definition zero_less_than_two''': 0 <= 2 :=
LESucc 0 1 (LESucc 0 0 (LERefl 0))
.
(* Here is a function that takes a natural number and adds two. *)
Definition add_two (n: nat): nat :=
S (S n)
.
Print add_two.
(* We can ask Coq to evaluate this function on some input. *)
Compute (add_two 0).
(* Another way to accomplish this is to write the term in proof mode! *)
Definition add_two' (n: nat): nat.
Proof.
Show Proof.
apply S.
Show Proof.
apply S.
Show Proof.
apply n.
Qed.
(* We can verify that the same program was built. *)
Print add_two'.
(* But something is off when we try to compute with it. *)
Compute (add_two' 0).
(* It turns out that proofs are opaque --- they do not unfold when computing.
In this case, we should close the proof using "Defined" instead. *)
Definition add_two'' (n: nat): nat.
Proof.
apply S.
apply S.
apply n.
Defined.
(* Now computation is possible. *)
Compute (add_two'' 0).
(* Okay, how about a lemma that has a premise? *)
Lemma shift_two (n m: nat):
n <= m -> n <= S (S m)
.
Proof.
intros.
apply LESucc.
apply LESucc.
apply H.
Qed.
(* The associated is now a *function* transforming one proof into another. *)
Print shift_two.
(* We can also define it directly, like this. *)
Definition shift_two' (n m: nat) (H: n <= m): n <= S (S m) :=
LESucc n (S m) (LESucc n m H)
.
(* Implications are truth transformers. For instance, the transitivity
property can be "implemented" by returning a function that feeds evidence
for the antecedent through both implications. *)
Definition impl_trans (P Q R: Prop) (H0: P -> Q) (H1: Q -> R): P -> R :=
fun (H2: P) => H1 (H0 H2)
.
(* Indeed, this is the type one would expect. *)
Check impl_trans.
(* What about True? It's an inductive with one (constant) constructor. *)
Print True.
(* We can always obtain a proof of True using the constant I. *)
Definition true: True := I.
(* How about disjunction? That's an inductive with two constructors: one for
the left disjunct, and one for the right; both require a proof. *)
Locate "\/".
Print or.
(* We can also construct proofs for disjunctions explicitly. *)
Definition true_or_false: True \/ False :=
or_introl I
.
(* A conjunction is an inductive with one constructor, which takes proofs for
both conjuncts as input. *)
Locate "/\".
Print and.
(* For instance, here is an explicit proof of a conjunction. *)
Definition true_and_true: True /\ True :=
conj I I
.
(* We can also explicitly construct the commutativity of implication. *)
Definition and_comm (P Q: Prop) (H: P /\ Q): Q /\ P :=
match H with
| conj HP HQ => conj HQ HP
end
.
(* Notice how this definition has the same type as our earlier proof! *)
Check and_comm.
(* The "if and only if" operator is a conjunction of implications. *)
Locate "<->".
Print iff.
(* That means that we can state this form of commutativity as follows. *)
Definition and_comm_iff (P Q: Prop): P /\ Q <-> Q /\ P :=
conj (and_comm P Q) (and_comm Q P)
.
(* Analogously, we can define commutativity of disjunction.
Homework --- Exercise 1: Complete the following definition.
Hint: Look at the definition of "or". It's an inductive with two
constructors. That means you should be able to perform a "match" on it
(analogous to and_comm above, but for different constructors). *)
Definition or_comm (P Q: Prop) (H: P \/ Q): Q \/ P :=
(* write your program here *)
.
(* Again, the type of this object matches the earlier proof. *)
Check or_comm.
(* False is an inductive with *no* constructors. *)
Print False.
(* Here is one reason why Coq does not allow non-terminating programs: the
program below may seem well-typed, because it promises to return an
instance of False, and does so by calling *itself*. But this would mean
that we could construct an instance of False by calling "falso 0" --- which
would allow us to prove everything! *)
Fail Fixpoint falso (n: nat): False := falso n.
(* We can convert a proof of P /\ Q -> R into a proof of P -> Q -> R. The idea
here is to return a function that takes proofs of P and Q, and returns a
proof of R. This function "calls" the hypothesis H with a proof of P /\ Q,
constructed by putting the proofs of P and Q together. *)
Definition curry (P Q R: Prop) (H: P /\ Q -> R): P -> Q -> R :=
fun (HP: P) (HQ: Q) => H (conj HP HQ)
.
(* Conversely, we can construct a proof of P /\ Q -> R from a proof of
P -> Q -> R, by returning a function that takes a proof of P /\ Q, and
uses the proofs of P and Q contained therein to "call" the hypothesis H.
Homework --- Exercise 2: complete the following definition.
Hint: Remember that your function should be returning another function.
You can use the "fun" syntax demonstrated above to accomplish this. *)
Definition uncurry (P Q R: Prop) (H: P -> Q -> R): P /\ Q -> R.
(* write your program here *)
.
(* Here is an object with a type that involves a "forall". It claims that for
all n, it holds that 0 <= n; on a typing level, this means that it takes
a number n, and returns a proof of 0 <= n. *)
Lemma zero_least: forall (n: nat), 0 <= n.
Proof.
intros.
induction n.
- apply LERefl.
- apply LESucc.
apply IHn.
Qed.
(* What does this proof look like as a term? It turns out that it uses the
induction principle nat_ind under the hood! *)
Print zero_least.
Check nat_ind.
(* The keyword "exists" is just notation for another inductive with just one
constructor, which takes a value n and a proof of P(n). *)
Locate "exists".
Unset Printing Notations.
Print ex.
Set Printing Notations.
(* Here is an example of a term that involves an existential. *)
Lemma zero_least_ex: exists (n: nat), n <= 0.
Proof.
exists 0.
apply LERefl.
Qed.
Print zero_least_ex.
(* Homework --- Exercise 3: Complete the following definition.
Hint: the input H that you get is a proof of (P \/ Q) \/ R. That means
you can perform a match on it as before; the left branch will be a proof
of P \/ Q, which requires a further match. Can you construct an
appropriate proof of P \/ (Q \/ R) in each branch? *)
Definition disj_associative (P Q R: Prop) (H: (P \/ Q) \/ R): P \/ (Q \/ R).
(* write your program here *)
.
(* Homework --- Exercise 4: Complete the following definition. *)
Definition conj_distributes (P Q R: Prop) (H: (P /\ Q) \/ (P /\ R)): P /\ (Q \/ R).
(* write your program here *)
.