(** * Software Foundations, Formally Benjamin C. Pierce Version of 9/24/2007 *) Require Export lec05_sol. (* ====================================================================== *) (** * LECTURE 4 *) Require String. Open Scope string_scope. (* Wei: The Case tactic is defined in a way slightly different from lec04. The theorem we are proving can be always seen as [H1, H2,..., Hn |- goal]. Pattern matching on goals enables us to manage hypotheses based on their patterns. Every rule in the pattern matching matches the hypotheses from right to left (unless [reverse]), and rules match from top to bottom. 'move_to_top' has one rule, which is _ |- _. So it matches the first hypothesis H1 whatever H1 is. a is "after" b if a appears below b in the list of hypothese. If a is already "after" b, "move a after b" will indeed "move" a "before" b. See http://pauillac.inria.fr/pipermail/coq-club/2007/003071.html *) Ltac move_to_top x := match reverse goal with | H : _ |- _ => try move x after H end. Ltac Case s := let c := fresh "case" in set (c := s); move_to_top c. (* ====================================================================== *) (** * LECTURE 5 *) Lemma length_snoc' : forall (X : Set) (v : X) (l : list X) (n : nat), length _ l = n -> length _ (snoc _ l v) = S n. Proof. intros X v l. induction l. Case "nil". intros n eq. rewrite <- eq. reflexivity. Case "cons". intros n eq. simpl. destruct n. Case "O". inversion eq. Case "S". assert (length X (snoc X l v) = S n). Case "Proof of assertion". apply IHl. inversion eq. reflexivity. rewrite -> H. reflexivity. Qed. Lemma sillyfun_odd : forall (n : nat), sillyfun n = yes -> odd n = yes. Proof. intro. rewrite sillyfun_no with (n:=n). intros. inversion H. Qed. (* ====================================================================== *) (** * LECTURE 6 *) (* Quick review... We've now seen a bunch of Coq's fundamental tactics -- enough, in fact, to do pretty much everything we'll want for a while. We'll introduce one or two more as we go along through the next few lectures, but basically this is the set we need. For quick reference, here's a summary: - intros move premises from goal to context - simpl simplify computations in the goal - simpl in H ... or a hypothesis - rewrite rewrite the goal - rewrite... in H ... or a hypothesis using an equational hypothesis or lemma - unfold replace a defined constant by its right-hand side in the goal - unfold... in H ... or a hypothesis - apply justify goal using a hypothesis, lemma, or constructor - apply... in H apply a hypothesis, lemma, or constructor to a hypothesis in the context (forward reasoning) - apply... with... explicitly specify values for variables that cannot be determined by pattern matching - destruct case analysis on values of inductively defined types - induction induction on values of inductively defined types - inversion reason by injectivity and distinctness of constructors - assert e as H introduce a "local lemma" e and call it H *) (* Note: many tactics (e.g., [apply], [reflexivity]) will automatically perform simplification before doing the rest of their work, so there is no need to put [simpl] before them explicitly. *) (* Quick summary of topics: What we've seen so far: - inductive definitions of datatypes - Fixpoints over inductive datatypes - higher-order functions (map, fold, filter, etc.) - polymorphism - basic Coq -- inductive proofs -- several fundamental tactics Still to come (before Midterm I): - subtleties of induction: generalizing IHs, induction principles - ``programming with propositions'' - logical connectives as inductive propositions - basic ideas of operational semantics *) (* ---------------------------------------------------------------------- *) (** * A closer look at induction *) (* The [induction] tactic actually does quite a bit of low-level bookkeeping for us. Recall the informal statement of the induction principle for natural numbers: If [P n] is some proposition involving a natural number n, and we want to show that P holds for ALL numbers n, we can reason like this: - show that [P O] holds - show that, if [P n] holds, then so does [P (S n)] - conclude that [P n] holds for all n. So, when we begin a proof with [intros n] and then [induction n], we are first telling Coq to consider PARTICULAR [n] (by introducing it into the context) and then telling it to prove something about ALL numbers (by using induction). What Coq actually does in this situation, internally, is to "re-generalize" the variable we perform induction on. For example, in the proof above that [plus] is associative... *) Lemma plus_assoc' : forall m n p : nat, plus m (plus n p) = plus (plus m n) p. Proof. (* ...we first introduce all three variables into the context, which amounts to saying "Choose an arbitrary [m], [n], and [p]..." *) intros m n p. (* ...We now use the [induction] tactic to prove [P m] (that is, [plus m (plus n p) = plus (plus m n) p]) for ALL [m], and hence also for the particular [m] that is in the context at the moment. *) induction m. Case "O". reflexivity. Case "S". (* In the second subgoal generated by [induction] -- the "inductive step" -- we must prove that [P m] implies [P (S m)] for all [m]. The [induction] tactic automatically introduces [m] and [P m] into the context for us, leaving just [P (S m)] as the goal. *) simpl. rewrite -> IHm. reflexivity. Qed. (* It also works to apply [induction] to a variable that is quantified in the goal. *) Lemma plus_commut' : forall m n : nat, plus m n = plus n m. Proof. induction m. Case "O". intros n. rewrite -> plus_zero. reflexivity. Case "S". intros n. simpl. rewrite -> IHm. rewrite -> dist_succ_plus. reflexivity. Qed. (* Note that [induction m] leaves [n] still bound in the goal -- i.e., what we are proving inductively is a statement beginning with [forall n]. *) (* If we do [induction] on a variable that is quantified in the goal AFTER some other quantifiers, the [induction] tactic will automatically introduce these quantifiers into the context. *) Lemma plus_commut'' : forall m n : nat, plus m n = plus n m. Proof. (* Let's do induction on [n] this time, instead of [m]... *) induction n. Case "O". simpl. rewrite -> plus_zero. reflexivity. Case "S". simpl. rewrite <- IHn. rewrite -> dist_succ_plus. reflexivity. Qed. (* ---------------------------------------------------------------------- *) (** * Generalizing induction hypotheses *) (* Last week's homework included a proof that the [double] function is injective. As many people discovered experimentally, the way we *start* this proof is a little bit delicate: if we begin it with [intros m. induction m.], all is well. But if we begin it with [intros m n. induction m.], we get stuck in the middle of the inductive case... *) Lemma double_injective_FAILED : forall m n, double m = double n -> m = n. Proof. intros m n. induction m. Case "O". simpl. intros eq. destruct n. Case "O". reflexivity. Case "S". inversion eq. Case "S". intros eq. destruct n. Case "O". inversion eq. Case "S". assert (m = n) as H. Case "Proof of assertion". (* Here we are stuck. We need the assertion in order to rewrite the final goal (subgoal 2 at this point) to an identity. But the induction hypothesis, [IHm], does not give us [m = n] -- there is an extra [S] in the way -- so the assertion is not provable. *) Admitted. (* What went wrong here? The problem is that, at the point we invoke the induction hypothesis, we have already introduced [n] into the context -- intuitively, we have told Coq, "Let's consider some particular [m] and [n]..." and we now have to prove that, if [double m = double n] for this *this particular* [m] and [n], then [m = n]. The next tactic, [induction m] says to Coq: We are going to show the goal by induction on [m]. That is, we are going to prove that P m = "if double m = double n, then m = n" holds for all [m] by showing - P O (i.e., "if double O = double n then O = n") - P m -> P (S m) (i.e., "if double m = double n then m = n" implies "if double (S m) = double n then S m = n"). If we look closely at the second statement, it is saying something rather strange: it says that, for any *particular* [n], if we know "if double m = double n then m = n" then we can prove "if double (S m) = double n then S m = n". To see why this is strange, let's think of a particular [n] -- say, [five]. The statement is then saying that, if we can prove Q = "if double m = ten then m = five" then we can prove R = "if double (S m) = ten then S m = five". But knowing Q doesn't give us any help with proving R! (If we tried to prove R from Q, we would say something like "Suppose [double (S m) = ten]..." but then we'd be stuck: knowing that [double (S m)] is [ten] tells us nothing about whether [double m] is [ten], so Q is useless at this point.) To summarize: Trying to carry out this proof by induction on [m] when [n] is already in the context doesn't work because we are trying to prove a relation involving *every* [m] but just a *single* [n]. *) (* The good proof of [double_injective] leaves [n] in the goal statement at the point where the [induction] tactic is invoked on [m]: *) Lemma double_injective' : forall m n, double m = double n -> m = n. Proof. intros m. induction m. Case "O". simpl. intros n eq. destruct n. Case "O". reflexivity. Case "S". inversion eq. Case "S". (* Notice that both the goal and the induction hypothesis have changed: the goal asks us to prove something more general (i.e., to prove the statement for *every* [n]), but the IH is correspondingly more flexible, allowing us to choose any [n] we like when we apply the IH. *) intros n eq. (* Now we choose a particular [n] and introduce the assumption that [double m = double n]. Since we are doing a case analysis on [m], we need a case analysis on [n] to keep the two "in sync". *) destruct n. Case "O". inversion eq. (* The zero case is trivial *) Case "S". (* At this point, since we are in the second branch of the [destruct n], the [n] mentioned in the context at this point is actually the predecessor of the one we started out talking about. Since we are also in the [S] branch of the induction, this is perfect: if we instantiate the generic [n] in the IH with the [n] that we are talking about right now (this instantiation is performed automatically by [apply]), then [IHm] gives us exactly what we need to finish the proof. *) assert (m = n) as H. Case "Proof of assertion". apply IHm. inversion eq. reflexivity. rewrite -> H. reflexivity. Qed. (* So what we've learned is that we need to be careful about using induction to try to prove something too specific: If we're proving a property of [m] and [n] by induction on [m], we may need to leave [n] generic. However, this strategy doesn't always apply directly. Suppose, for example, that we had decided we wanted to prove [double_injective] by induction on [n] instead of [m]. *) Lemma double_injective_take2_FAILED : forall m n, double m = double n -> m = n. Proof. intros m n. induction n. Case "O". simpl. intros eq. destruct m. Case "O". reflexivity. Case "S". inversion eq. Case "S". intros eq. destruct m. Case "O". inversion eq. Case "S". assert (m = n) as H. Case "Proof of assertion". (* Here we are stuck again, just like before. *) Admitted. (* The problem is that, to do induction on [n], we must first introduce [m]. (If we simply say [induction n] without introducing anything first, Coq will automatically introduce [m] for us!) What can we do about this? One possibility is to rewrite the statement of the lemma so that [n] is quantified before [m]. This will work, but it's not nice: We don't want to have to mangle the statements of lemmas to fit the needs of a particular strategy for proving them -- we want to state them in the most clear and natural way. What we can do instead is to first introduce all the quantified variables and then RE-GENERALIZE one or more of them, taking them out of the context and putting them back at the beginning of the goal. The [generalize dependent] tactic does this. *) Lemma double_injective_take2 : forall m n, double m = double n -> m = n. Proof. intros m n. (* [m] and [n] are both in the context *) generalize dependent m. (* Now [m] is back in the goal and we can do induction on [n] and get a sufficiently general IH. *) induction n. Case "O". simpl. intros m eq. destruct m. Case "O". reflexivity. Case "S". inversion eq. Case "S". intros m eq. destruct m. Case "O". inversion eq. Case "S". assert (m = n) as H. Case "Proof of assertion". Case "Proof of assertion". apply IHn. inversion eq. reflexivity. rewrite -> H. reflexivity. Qed. Lemma plus_m_m_injective_take2 : forall m n, plus m m = plus n n -> m = n. Proof. (* Carry out this proof by induction on [n]. *) (* Wei: The order of 'generalize' and 'induction' is very important, because if we generalize after induction, only the base case is generalized. *) intros. generalize dependent m. induction n. destruct m. trivial. simpl. intros. inversion H. destruct m. intros. inversion H. intros. rewrite dist_succ_plus in H. rewrite dist_succ_plus in H. simpl in H. inversion H. rewrite (IHn m). trivial. trivial. Qed. Lemma length_snoc''' : forall (n : nat) (X : Set) (v : X) (l : list X), length _ l = n -> length _ (snoc _ l v) = S n. Proof. (* Prove this by induction on [l]. *) intros. generalize dependent n. induction l. (* l=nil *) intros. rewrite <- H. trivial. (* l!=nil *) simpl. intros. destruct n. inversion H. apply S_inj in H. rewrite (IHl n). trivial. trivial. Qed. Lemma eqnat_no_S : forall m n, eqnat m n = no -> eqnat (S m) (S n) = no. Proof. (* Prove this by induction on [n]. *) intros. generalize dependent m. induction n. trivial. simpl. trivial. Qed. Lemma nth_after_last : forall (n : nat) (X : Set) (l : list X), length _ l = n -> nth _ (S n) l = None _. Proof. (* Prove this by induction on [l] *) intros. generalize dependent n. induction l. trivial. simpl. intros. destruct n. inversion H. apply S_inj in H. rewrite (IHl n H). trivial. Qed. Lemma length_append_cons : forall (X : Set) (l1 l2 : list X) (x : X) (n : nat), length _ (l1 ++ (x :: l2)) = n -> S (length _ (l1 ++ l2)) = n. Proof. (* Prove this by induction on [l1] *) intros. generalize dependent n. induction l1. trivial. simpl. intros. destruct n. inversion H. apply S_inj in H. rewrite (IHl1 n H). trivial. Qed. (* ====================================================================== *) (** * Programming with Propositions *) (* A PROPOSITION is a factual claim. In Coq, propositions are written as expressions of type Prop. *) Check (plus two two = four). (* So far, all the propositions we have seen are equality propositions. But we can build on equality propositions to make other sorts of claims. For example, what does it mean to claim that "a number n is even"? We have a function that (we believe) tests evenness, so one possible definition is "n is even if (even n = yes)." We can capture this idea as follows: *) Definition evenE (n:nat) := even n = yes. (* [evenE] is a PARAMETERIZED PROPOSITION. Think of it as a function that, when applied to a number n, yields a proposition asserting that n is even. *) Check evenE. Check (evenE four). (* If we can give evidence for a proposition, is is said to be PROVABLE. For example, the proposition [evenE four] is provable, and the evidence is that we can show [even four] is equal to [yes]. *) Lemma evenE_four : evenE four. Proof. (* Note that we need to [unfold evenE] to enable [simpl]. *) unfold evenE. simpl. reflexivity. Qed. (* Both provable and unprovable claims are perfectly good propositions. Simply BEING a proposition is one thing; being PROVABLE is something else! *) Check (evenE two). Check (evenE three). (* At this point, we have two rather different ways of talking about evenness: - a function [even] that CALCULATES evenness - a parameterized proposition [evenE] that ASSERTS evenness, defined in terms of the [even] function. We can now give EVIDENCE for the provability of an [evenE] proposition by showing that the [even] function returns [yes]. *) (* -------------------------------- *) (* Inductively defined propositions *) (* There is yet another way of talking about assertions of evenness. Instead of going "indirectly" via the [even] function, we can give a "direct" definition of evenness by saying, straight out, what we would be willing to accept as EVIDENCE that a given number is even. *) Inductive evenI : nat -> Prop := | even_zero : evenI O | even_SS : forall n:nat, evenI n -> evenI (S (S n)). (* Let's examine the parts of this definition: - The first line declares that [evenI] is a proposition parameterized by a natural number (i.e., it is the same sort of thing as [evenE]) - The second line declares that the constructor [even_zero] can be taken as evidence for the assertion [evenI O]. - The third line declares that, if [n] is a number and [E] is evidence that [n] is even (i.e., [E] is evidence for the assertion [evenI n]), then [even_SS n E] can be taken as evidence for [evenI (S (S n))]. That is, we can construct evidence that [S (S n)] is even by applying the constructor [even_SS] to evidence that [n] is even. *) (* These two constructors can be combined to produce evidence of the evenness of particular numbers. *) Lemma four_even : evenI four. Proof. apply even_SS. apply even_SS. apply even_zero. Qed. (* We can also prove implications involving evenness in this style. For example, the assertion [evenI n -> evenI (plus four n)] can be read "assuming we have evidence that [n] is even, we can construct evidence that [plus 4 n] is even." *) Lemma even_plus4 : forall n, evenI n -> evenI (plus four n). Proof. intros n E. simpl. apply even_SS. apply even_SS. apply E. Qed. Lemma double_even : forall n, evenI (double n). Proof. induction n. unfold double. exact even_zero. apply even_SS with (n:=(double n)). exact IHn. Qed. (* Besides CONSTRUCTING evidence of evenness, we can also REASON ABOUT evidence of evenness. The fact that we introduced [evenI] with an [Inductive] declaration tells us not only that the two constructors can be used to build evidence of evenness but that these two constructors are the ONLY ways that evidence of evenness can be built. In other words, if someone gives us a number [n] and evidence [E] justifying the assertion [evenI n], then we know that [E] can only have one of two forms: either [E] is [even_zero] (and [n] is [O]), or [E] is [even_SS m E'], where [n = S (S m)] and [E'] is evidence that [n] is even. This means that it makes sense to use all the tactics that we have already seen for inductively defined DATA to reason about inductively defined EVIDENCE. For example, here we perform induction on evidence that [n] is even to show that the old [even] function returns [yes] on [n]. *) Lemma evenI_evenE : forall n, evenI n -> evenE n. Proof. intros n E. induction E. Case "even_zero". unfold evenE. reflexivity. Case "even_SS". apply IHE. Qed. (* Thought question: Could this proof be carried out by induction on [n] instead of [E]? *) (* Similarly, we can perform [inversion] on evidence of evenness. *) Lemma SSeven_even : forall n, evenI (S (S n)) -> evenI n. Proof. intros n E. inversion E. apply H0. Qed. Lemma SSSSeven_even : forall n, evenI (S (S (S (S n)))) -> evenI n. Proof. intros. inversion H. inversion H1. trivial. Qed. Lemma even5_nonsense : evenI five -> plus two two = nine. Proof. intros. inversion H. inversion H1. inversion H3. Qed. (* ----------------- *) (* The discussion so far has shown that the proposition that "a number is even" can be phrased in two different ways -- indirectly, via a testing function [even], or directly, by inductively describing what constitutes evidence for evenness. These two ways of defining evenness are about equally easy to state and work with. However, for many other properties of interest, the direct inductive definition is much simpler, because writing a testing function may be awkward or even impossible. For example, consider the property MyProp defined as follows: 1) the number 4 has property MyProp 2) if n has property MyProp, then so does 4+n 3) if n+2 has property MyProp, then so does n 4) no other numbers have property MyProp This is a perfectly sensible definition of a set of numbers, but we cannot write this definition directly as a Coq Fixpoint (or as a recursive function in any other programming language). We might be able to find a clever way of testing this property using a Fixpoint (indeed, it is not even too hard to find one in this case), but in general this could require arbitrarily much thinking. In fact, if the property we are interested in is uncomputable, then we cannot define it as a Fixpoint no matter how hard we try, because Coq requires that all Fixpoints correspond to terminating computations. On the other hand, an inductive definition of what it means to give evidence for the property MyProp is straightforward: *) Inductive MyProp : nat -> Prop := | MyProp1 : MyProp four | MyProp2 : forall n:nat, MyProp n -> MyProp (plus four n) | MyProp3 : forall n:nat, MyProp (plus two n) -> MyProp n. (* The first three clauses in the informal definition of MyProp above are reflected in the first three clauses of the inductive definition. The fourth clause is the precise force of the keyword [Inductive]. *) (* As we did with evenness, we can now construct evidence that certain numbers satisfy [MyProp]. *) Lemma MyProp_eight : MyProp eight. Proof. (* Wei: or simply: [apply MyProp2 with (n:=four); exact MyProp1.] *) apply MyProp3. simpl. assert (ten = plus four six) as H. Case "Proof of assertion". reflexivity. rewrite -> H. apply MyProp2. assert (six = plus four two) as H0. Case "Proof of assertion". reflexivity. rewrite -> H0. apply MyProp2. apply MyProp3. simpl. apply MyProp1. Qed. Lemma MyProp_zero : MyProp zero. Proof. apply MyProp3. apply MyProp3. apply MyProp1. Qed. Lemma MyProp_plustwo : forall n:nat, MyProp n -> MyProp (S (S n)). Proof. intros. apply MyProp3. simpl. change (MyProp (plus four n)). apply MyProp2. exact H. Qed. (* With these, we can show that MyProp holds of all even numbers, and vice versa. *) Lemma MyProp_even : forall n:nat, evenI n -> MyProp n. Proof. intros n H. induction H. apply MyProp_zero. apply MyProp_plustwo. apply IHevenI. Qed. Lemma even_MyProp : forall n:nat, MyProp n -> evenI n. Proof. intros. induction H. exact four_even. apply even_plus4. trivial. inversion IHMyProp. trivial. Qed. (* ---------------------------------------------------------------------- *) (* Induction principles *) (* Every time we define something with an [Inductive] declaration, Coq automatically generates an induction principle. The [induction] tactic invokes this principle for us automatically, but we are also free to invoke it ourselves if we like. The induction principle for a type [t] is called [t_ind]. Here is the one for natural numbers: *) Check nat_ind. (* The fact [nat_ind] is stored in Coq's global environment just like any other lemma we have proved. If we like, we can even invoke it directly with an [apply] tactic. *) Lemma times_zero' : forall n:nat, times n zero = zero. Proof. apply nat_ind. Case "O". simpl. reflexivity. Case "S". simpl. intros n IHn. rewrite -> IHn. simpl. reflexivity. Qed. (* Note that, in the induction case, we have to do a little bookkeeping (the [intros]) manually that [induction] does automatically. Also, note that we do not introduce [n] into the context before applying [nat_ind] -- the conclusion of [nat_ind] is a quantified formula, and [apply] needs this conclusion to match the shape of our goal state. *) Lemma plus_one' : forall n:nat, plus n one = S n. Proof. (* Complete this proof without using the [induction] tactic. *) apply nat_ind. trivial. intros. simpl. rewrite H. trivial. Qed. Lemma plus_assoc'' : forall m n p : nat, plus m (plus n p) = plus (plus m n) p. Proof. intros m n p. (* Complete this proof without using the [induction] tactic. Don't change the [intros] in the first line. *) generalize dependent m. apply nat_ind. trivial. intros. simpl. rewrite H. trivial. Qed. (* The induction principles for inductively defined propositions like [evenI] are a tiny bit more complicated. Intuitively, this is because we want to use them to prove things by inductively considering the possible shapes that something in [evenI] can have -- either it is evidence that [zero] is even, or else it is evidence that, for some [n], [S (S n)] is even, and it includes evidence that [n] itself is. But the things we want to prove are not statements about EVIDENCE, they are statements about NUMBERS. So we want an induction principle that allows reasoning by induction on the former to prove properties of the latter. In English, it goes like this: - Suppose, P is a predicate on natural numbers (that is, [P n] is a [Prop] for every [n]). To show that [P n] holds whenever [n] is even, it suffices to show: -- [P] holds for [zero] -- for any [n], if [n] is even and [P] holds for [n], then [P] holds for [S (S n)]. Formally: *) Check evenI_ind. (* We can apply [evenI_ind] directly instead of using [induction], following pretty much the same pattern as above. *) Lemma evenI_evenE' : forall n, evenI n -> evenE n. Proof. apply evenI_ind. Case "even_zero". unfold evenE. reflexivity. Case "even_SS". intros n H IHE. unfold evenE. apply IHE. Qed. (* EXERCISE (not to be handed in, but strongly recommended -- there will be questions like this on the midterm!). Write out the induction principles that Coq will generate for the inductive declarations [list] and [MyProp]. Compare your answers against the results Coq prints for the following queries. *) Check list_ind. Check MyProp_ind. Lemma even_MyProp' : forall n:nat, MyProp n -> evenI n. Proof. (* Complete this proof without using the [induction] tactic. *) apply MyProp_ind; intros. exact four_even. apply even_plus4. trivial. inversion H0. trivial. Qed.