(** * Software Foundations, Formally Benjamin C. Pierce Version of 9/17/2007 *) Require Export lec03_sol. (** * LECTURE 3 *) (* Exercise: Which of the following lemmas require induction in their proofs? (See if you can predict in advance, before trying with Coq.) *) Lemma S_neq_zero : forall n:nat, eqnat (S n) zero = no. Proof. (* SOLUTION *) simpl. reflexivity. Qed. Lemma zero_neq_S : forall n:nat, eqnat zero (S n) = no. Proof. (* SOLUTION *) simpl. reflexivity. Qed. Lemma n_neq_Sn : forall n : nat, eqnat n (S n) = no. Proof. (* SOLUTION *) intros n. induction n. simpl. reflexivity. simpl. rewrite -> IHn. reflexivity. Qed. (* EXERCISE: - Find a non-trivial equation involving cons (::), snoc, and append (++). - Prove it. FILL IN HERE *) (* HIDE WHEN EXERCISE *) Lemma append_snoc : forall X : Set, forall l1 l2 : list X, forall v : X, ((snoc _ l1 v) ++ l2) = l1 ++ (v :: l2). Proof. intros X l1 l2 v. induction l1. simpl. reflexivity. simpl. rewrite -> IHl1. reflexivity. Qed. (* ====================================================================== *) (** * LECTURE 4 *) (* The small set of tactics we have seen so far can already be used to prove some fairly interesting properties of inductively defined functions. In this lecture we extend our range by introducing several other fundamental tactics and exploring how they can be used for more sophisticated reasoning involving equalities and induction. *) (* Note: As we get into Coq more seriously, using a richer set of tactics and attacking harder problems, you will probably find yourself getting stuck more often. This is when your study partner or group will start being extremly useful: two or three people tend to get unstuck exponentially faster than one person working alone. If you are not in a study group and want to be, please let us know and we will facilitate. If you are happy working solo, that's OK. But make sure that you at least have some phone numbers of people you can call when you are feeling frustrated. In either case, you should at least attempt all the homework problems before Friday's recitions, if at all possible, so that you can ask questions there. *) (* ---------------------------------------------------------------------- *) (** * The [Case] annotation *) (* One of the less nice things about Coq's mechanisms for interactive proof is the way subgoals seem to come and go almost at random, with no explicit indication of where we are -- which case of an induction or case analysis we are in -- at any given moment. In very short proofs, this is not a big deal. But in more complex proofs it can become quite difficult to stay oriented. Here is a simple hack that I find helps things quite a bit. It uses some facilities of Coq that we have not discussed -- the string library (just for the concrete syntax of quoted strings) and the [Ltac] command, which allows us to declare custom tactics. We will come back to [Ltac] in more detail later. For now, don't worry about the details of this declaration. *) Require String. Open Scope string_scope. (* Wei: create a fresh hypothesis named "case" (in contrast to those derived from "H"!), and set it to s. By looking at this hypo, you can see which case you are in. It has no real effect. fresh: http://coq.inria.fr/V8.1/refman/Reference-Manual011.html#@default659 set: http://coq.inria.fr/V8.1/refman/Reference-Manual010.html#@tactic22 *) Ltac Case s := let c := fresh "case" in set (c := s). (* Having defined the [Case] tactic, let's see how it is used. Step through the following proof and observe how the context changes. *) Lemma length_reverse' : forall X : Set, forall l : list X, length _ l = length _ (reverse _ l). Proof. intros X l. induction l. Case "nil". simpl. reflexivity. Case "cons". simpl. rewrite -> length_snoc. rewrite -> IHl. reflexivity. Qed. (* The [Case] tactic does something very trivial: It simply adds a string that we choose (tagged with the identifier "case") to the context for the current goal. When subgoals are generated, this string is carried over into their contexts. When the last of these subgoals is finally proved and the next top-level goal (a sibling of the current one) becomes active, this string will no longer appear in the context and we will be able to see that the case where we introduced it is complete. *) (* ---------------------------------------------------------------------- *) (** * The [unfold] tactic *) (* Sometimes the [simpl] tactic doesn't make things quite as simple as we need. The [unfold] tactic can be used to explicitly replace a defined name by the right-hand side of its definition. *) Definition plus3 (n:nat) := plus n three. Lemma plus3_example : forall n, plus3 n = plus three n. Proof. intros n. induction n. Case "O". simpl. reflexivity. Case "S". simpl. (* ... doesn't give us what we need *) unfold plus3. unfold plus3 in IHn. simpl. rewrite -> IHn. simpl. reflexivity. Qed. (* Exercise (not to be handed in): Which of the uses of [simpl] in the above proof are required? Which can be deleted? *) (* ---------------------------------------------------------------------- *) (** * The [apply] tactic *) (* We often encounter situations where the goal to be proved is exactly the same as some hypothesis in the context or some previously proved lemma. *) Lemma silly1 : forall (m n o p : nat), m = n -> [m,o] = [m,p] -> [m,o] = [n,p]. Proof. intros m n o p eq1 eq2. rewrite <- eq1. (* At this point, we could finish with [rewrite -> eq2. reflexivity.] as we have done several times above. But we can achieve the same effect in a single step by using the [apply] tactic instead: *) apply eq2. Qed. (* The [apply] tactic also works with CONDITIONAL hypotheses and lemmas: if the statement being applied is an implication, then the premises of this implication will be added to the list of subgoals needing to be proved. *) Lemma silly2 : forall (m n o p : nat), m = n -> (forall (q r : nat), q = r -> [q,o] = [r,p]) -> [m,o] = [n,p]. Proof. intros m n o p eq1 eq2. apply eq2. apply eq1. Qed. (* You may find it instructive to experiment with this proof and see if there is a way to complete it using just [rewrite] instead of [apply]. *) (* Typically, when we use [apply H], the statement [H] will begin with a [forall] binding some "universal variables." When Coq matches the current goal against the conclusion of [H], it will try to find appropriate values for these variables. For example, when we do [apply eq2] in in the following proof, the universal variable [q] in [eq2] gets instantiated with [(m,m)] and [r] gets instantiated with [(n,n)]. (What does [X] get instantiated with?) *) Lemma silly2a : forall (m n : nat), (m,m) = (n,n) -> (forall (X : Set) (q r : X), q = r -> [q] = [r]) -> [(m,m)] = [(n,n)]. Proof. intros m n eq1 eq2. apply eq2. apply eq1. Qed. (* Exercise: Complete the following proof without using [simpl]. *) Lemma silly_ex : (forall n, even n = yes -> odd (S n) = yes) -> even three = yes -> odd four = yes. Proof. intros. apply H. trivial. Qed. (* To use the [apply] tactic, the (conclusion of the) fact being applied must match the goal EXACTLY -- for example, [apply] will not work if the left and right sides of the equality are swapped. *) Lemma silly3_firsttry : forall (m : nat), (eqnat m five = yes -> eqnat (S (S m)) seven = yes) -> yes = eqnat m five -> yes = eqnat (S (S m)) seven. Proof. intros m eq H. (* here we are stuck *) Admitted. (* When you find yourself in such a situation, one thing to do is to go back and try changing reorganizing the statement of whatever you are trying to prove so that things appear the right way around when they are needed. But if this is not possible or convenient, then the following lemma can be used to swap the sides of an equality statement in the goal so that some other lemma can be applied. *) Lemma eq_symm : forall (X:Set) (m n : X), m = n -> n = m. Proof. intros X m n eq. rewrite -> eq. reflexivity. Qed. Lemma silly3 : forall (m : nat), (eqnat m five = yes -> eqnat (S (S m)) seven = yes) -> yes = eqnat m five -> yes = eqnat (S (S m)) seven. Proof. intros m eq H. apply eq_symm. apply eq. apply eq_symm. apply H. Qed. Lemma reverse_exercise1 : forall (X : Set) (l l' : list X), l = reverse X l' -> l' = reverse X l. Proof. intros. rewrite H. apply eq_symm. apply reverse_reverse. Qed. (* ---------------------------------------------------------------------- *) (** * Using tactics on hypotheses *) (* By default, most tactics perform some operation on the goal formula and leave the context unchanged. But tactics often come with variants that can be used to perform similar operations on a statement in the context. *) (* For example, the tactic [simpl in H] performs simplification in the hypothesis named [H] in the context. *) Lemma S_inj' : forall (m n : nat) (b : yesno), eqnat (S m) (S n) = b -> eqnat m n = b. Proof. intros m n b H. simpl in H. apply H. Qed. (* Similarly, the tactic [apply L in H] matches some conditional statement [L] (of the form [L1->L2], say) against a hypothesis H in the context. However, unlike ordinary [apply] (which rewrites a goal matching [L2] into a subgoal [L1]), [apply L in H] matches [H] against [L1] and, if successful, replaces it with [L2]. In other words, [apply L in H] gives us a form of FORWARD REASONING -- from [L1->L2] and a hypothesis matching [L1], it gives us a hypothesis matching [L2]. By contrast, [apply L] is BACKWARD REASONING -- it says that if we know [L1->L2] and we are trying to prove [L2], it suffices to prove [L1]. *) (* Here is a variant of a proof from above, using forward reasoning throughout intead of backward reasoning. *) Lemma silly3' : forall (m : nat), (eqnat m five = yes -> eqnat (S (S m)) seven = yes) -> yes = eqnat m five -> yes = eqnat (S (S m)) seven. Proof. intros m eq H. apply eq_symm in H. apply eq in H. apply eq_symm in H. apply H. Qed. (* ---------------------------------------------------------------------- *) (** * The [assert] tactic *) (* Coq's default style of reasoning is backward. However, it can sometimes be convenient to switch to a "forward mode" for part of a proof. Here is one more tactic that does this in a very powerful way... *) (* It is common to reach a point in a proof where we would like to apply some fact [L] that we have not proven yet. One way to deal with this is to abort the current proof, state [L] as a separate lemma, prove it, and then go back to where we were. But this strategy may sometimes not be attractive: it may be that [L] depends on many local assumptions, so that stating it as a separate lemma would require adding many awkward hypotheses; or it may be that the proof of [L] is short or uninteresting. The [assert] tactic helps in such cases. If the current goal statement is [G], then doing [assert L as H] leaves us with two subgoals: - proving [L] - proving [G] again, but now with [L] as a hypothesis (named [H]) in the context. For example, here is an alternative proof of the [reverse_reverse] lemma, with the proof of the auxiliary [reverse_snoc] lemma in-lined using [assert]. *) Lemma reverse_reverse' : forall X : Set, forall s : list X, reverse X (reverse X s) = s. Proof. intros X s. induction s. Case "nil". simpl. reflexivity. Case "cons". simpl. assert (forall Y : Set, forall v : Y, forall l : list Y, reverse Y (snoc Y l v) = v :: (reverse _ l)) as reverse_snoc. Case "Proof of reverse_snoc". intros Y v l. induction l. Case "nil". reflexivity. Case "cons". simpl. rewrite -> IHl. reflexivity. rewrite -> reverse_snoc. rewrite -> IHs. reflexivity. Qed. (* In the previous proof, [reverse_snoc] was inlined "verbatim" from above, for simplicity. But this example is also an illustration of how asserting needed facts in-line can be simpler than breaking them out as separate lemmas: we can simplify the statement of [reverse_snoc] by omitting the quantification over [Y] -- essentially, proving just the version of [reverse_snoc] that we need right here (where the elements of the list have type [X]) rather than a more general one. *) Lemma reverse_reverse'' : forall X : Set, forall s : list X, reverse X (reverse X s) = s. Proof. intros X s. induction s. Case "nil". simpl. reflexivity. Case "cons". simpl. assert (forall v : X, forall l : list X, reverse X (snoc X l v) = v :: (reverse _ l)) as reverse_snoc. Case "Proof of reverse_snoc". intros v l. induction l. Case "nil". reflexivity. Case "cons". simpl. rewrite -> IHl. reflexivity. rewrite -> reverse_snoc. rewrite -> IHs. reflexivity. Qed. (* ---------------------------------------------------------------------- *) (** * The [inversion] tactic *) (* A fundamental property of inductive definitions is that their constructors are INJECTIVE. For example, the only way we can have [S n = S m] is if [m = n]. The [inversion] tactic uses this injectivity principle to 'destruct' an equality hypothesis. *) Lemma S_inj : forall (m o : nat), S m = S o -> m = o. Proof. intros m o eq. inversion eq. reflexivity. (* Wei: This can also be proved by the injection tactics *) Restart. intros. injection H. trivial. Qed. (* The same principle applies to other inductively defined sets, such as lists. *) Lemma silly4 : forall (m o : nat), [m] = [o] -> m = o. Proof. intros m o eq. inversion eq. reflexivity. Qed. (* The [inversion] tactic will destruct equalities between complex values, binding multiple variables as it goes. *) Lemma silly5 : forall (m n o : nat), [m,n] = [o,o] -> [m] = [n]. Proof. intros m n o eq. inversion eq. reflexivity. Qed. (* ADD: An exercise. *) (* Another critical property of inductive definitions is that distinct constructors are never equal, no matter what they are applied to. For example, [S n] can never be equal to [O], no matter what [n] is. This means that anytime we can see a HYPOTHESIS of the form [S n = O], we know that we must have made contradictory assumptions at some point. The [inversion] tactic can be used to cut off the proof at this point. *) Lemma silly6 : forall (n : nat), S n = O -> plus two two = five. Proof. intros n contra. inversion contra. Qed. (* Similarly, under the assumption that [no = yes], anything at all becomes provable. *) Lemma silly7 : forall (m n : nat), no = yes -> [m] = [n]. Proof. intros m n contra. inversion contra. Qed. (* Here is a more interesting use of inversion to prove a property that we will find useful at many points later on... *) Lemma eqnat_yes : forall m n, eqnat m n = yes -> m = n. Proof. intros m. induction m. Case "O". intros n. destruct n. Case "O". simpl. reflexivity. Case "S". simpl. intros contra. inversion contra. Case "S". intros n. destruct n. Case "O". intros contra. inversion contra. Case "S". intros H. simpl in H. apply IHm in H. rewrite -> H. reflexivity. Qed. (* Note how nested uses of our [Case] tactic behave: when there is already a "case" tag in the context, it adds a new tag called "case0" (then "case1", etc.). In this way we can keep track of nested cases to any depth we like. *) (* ADD: An exercise. *) (* ---------------------------------------------------------------------- *) (** * The [remember] tactic *) Definition sillyfun (n : nat) : yesno := if eqnat n three then no else if eqnat n five then no else no. Lemma sillyfun_no : forall (n : nat), sillyfun n = no. Proof. intros n. unfold sillyfun. remember (eqnat n three) as e. destruct e. Case "yes". reflexivity. Case "no". remember (eqnat n five) as e. destruct e. Case "yes". reflexivity. Case "no". reflexivity. Qed. Lemma sillyfun_odd : forall (n : nat), sillyfun n = yes -> odd n = yes. Proof. intros. rewrite sillyfun_no in H. inversion H. Qed. (* Exercise: Here is a slightly different silly function definition and a simple fact to be proved about it. *) Definition sillyfun1 (n : nat) : yesno := if eqnat n three then yes else if eqnat n five then yes else no. Lemma sillyfun1_odd : forall (n : nat), sillyfun1 n = yes -> odd n = yes. Proof. intros. unfold sillyfun1 in H. remember (eqnat n three) as e. destruct e. Case "n=3". apply eq_symm in Heqe. apply eqnat_yes in Heqe. rewrite Heqe. trivial. remember (eqnat n five) as e. destruct e. Case "n=5". apply eq_symm in Heqe0. apply eqnat_yes in Heqe0. rewrite Heqe0. trivial. Case "n!=3 and n!=5". inversion H. Qed. (* Wei: filter's definition from lec02: Fixpoint filter (X:Set) (test: X->yesno) (l:list X) {struct l} : (list X) := match l with | nil => nil _ | hd::tl => match test hd with | yes => hd :: (filter _ test tl) | no => filter _ test tl end end. *) Lemma filter_exercise : forall (X : Set) (test : X -> yesno) (x : X) (l l' : list X), filter _ test l = x :: l' -> test x = yes. Proof. intros. induction l. Case "l=[]". simpl in H. inversion H. Case "l=x0::l'". remember (test x0) as e. destruct e. Case "x0=x". apply eq_symm in Heqe. unfold filter in H. rewrite Heqe in H. fold filter in H. inversion H. rewrite <- H1. exact Heqe. Case "x0!=x". apply eq_symm in Heqe. unfold filter in H. rewrite Heqe in H. fold filter in H. apply IHl. exact H. Qed.