(** * Software Foundations, Formally Benjamin C. Pierce Version of 10/17/2007 Before handing in this file with your homework solutions, please fill in the names of all members of your group: FILL IN HERE Also, please tell us roughly how many person-hours you spent on this assignment (i.e., if you worked in a group, give us the SUM of the number of hours spent by each person individually). FILL IN HERE *) (* ====================================================================== *) (* LECTURE 8 *) (* Technical note: This proof script has finally gotten too big and unwieldy to keep in a single file. This file contains just this week's lectures. Material from all previous lectures is contained in the file [lec07.v] that was distributed last week. You should COMPILE this file (so that it can be loaded quickly) by typing the following command to your shell: coqc lec07.v This generates a file [lec07.vo], which can now be loaded into the working environment using the following command: *) Require Export lec07. (* ---------------------------------------------------------------------- *) (* A little more on induction principles *) (* The relation between [Inductive] definitions and the corresponding induction principles generated for them by Coq is somewhat complex to describe in full generality. It is not necessary to understand every detail, but let's discuss a few points... The most typical example of an inductive definition is, of course, natural numbers: Inductive nat : Set := | O : nat | S : nat -> nat. The corresponding induction principle that is generated by Coq... nat_ind : forall P : nat -> Prop, P O -> (forall n : nat, P n -> P (S n)) -> forall n : nat, P n ... can be read as follows: - The induction principle can be used to prove assertions about numbers; the type [nat->Prop] of its first argument [P] captures this. - When the induction principle is applied, the result will be a proof that [P] holds of all numbers; the last line says this. - When the induction principle is applied to some [P], two subgoals are generated (one for each of the constructors of [nat]); these are the second and third lines. The inductive definition of polymorphic lists Inductive list (X:Set) : Set := | nil : list X | cons : X -> list X -> list X. is very similar. The main difference is that, here, the whole definition is PARAMTERIZED on a set [X] -- i.e., we are defining a FAMILY of inductive types [list X], one for each [X]. Note that, wherever [list] appears in the body of the declaration, it is always applied to the parameter [X]. The induction principle is likewise parameterized on [X]: list_ind : forall (X : Set) (P : list X -> Prop), P (nil X) -> (forall (x : X) (l : list X), P l -> P (x :: l)) -> forall l : list X, P l Note the wording here (and, accordingly, the form of [list_ind]): The WHOLE induction principle is parameterized on [X]. That is, [list_ind] can be thought of as a polymorphic function that, when applied to a set [X], gives us back an induction principle specialized to [list X]. *) (* THOUGHT EXERCISE (not to be handed in): Write out the induction principles that Coq will generate for the following definitions of inductive sets. Compare your answers with what Coq prints. *) Inductive rgb : Set := | red : rgb | green : rgb | blue : rgb. Check rgb_ind. Inductive tree (X:Set) : Set := | leaf : nat -> tree X | node : tree X -> tree X -> tree X. Check tree_ind. (* THOUGHT EXERCISE (not to be handed in): Find an inductive definition that gives rise to the following induction principle: mytype_ind : forall (X : Set) (P : mytype X -> Prop), (forall x : X, P (constr1 X x)) -> (forall n : nat, P (constr2 X n)) -> (forall m : mytype X, P m -> forall n : nat, P (constr3 X m n)) -> forall m : mytype X, P m *) (* THOUGHT EXERCISE (not to be handed in): Find an inductive definition that gives rise to the following induction principle: foo_ind : forall (X Y : Set) (P : foo X Y -> Prop), (forall x : X, P (bar X Y x)) -> (forall y : Y, P (baz X Y y)) -> (forall f1 : nat -> foo X Y, (forall n : nat, P (f1 n)) -> P (quux X Y f1)) -> forall f2 : foo X Y, P f2 *) (* Here is a more interesting inductively defined family of sets: Inductive vector (X:Set) : nat->Set := | Vnil : vector X zero | Vcons : forall n:nat, X -> vector X n -> vector X (S n). Given a set [X] and a natural number [n], the set [vector X n] describes lists of length [n] whose elements are drawn from the set [X]. Note that the numeric argument to [vector] is NOT a general parameter: it plays different roles in the two clauses (it is [zero] in the type of the constructor [Vnil] and [S n] in the type of [Vcons]). We call such an argument an INDEX of the inductive definition. Since it is not used uniformly, it cannot be written as a parameter to the whole inductive definition (as [X] is). Instead, it becomes part of the declared "result type" of the inductive definition (i.e., the result type is [nat->Set], not just [Set]). Thus, the first line of the declaration, [Inductive vector (X:Set) : nat->Set], can be read as, "For every set [X], declare a family of inductively defined sets (indexed by a [nat]) as follows..." Terminological note: Like [list], the type [vector] is parameterized -- it can be thought of as a function that, when applied to appropriate arguments, yields a set. Unlike [list], however, one of [vector]'s parameters is a VALUE, not just another set. Types like this -- i.e., families of types indexed by values -- are generally called DEPENDENT TYPES. Here is the induction principle that is generated for [vector]: vector_ind : forall (X : Set) (P : forall n : nat, vector X n -> Prop), P zero (Vnil X) -> (forall (n : nat) (x : X) (v : vector X n), P n v -> P (S n) (Vcons X n x v)) -> forall (n : nat) (v : vector X n), P n v. Note that the parameter [X] and the index [n] are treated differently: [X] becomes a parameter to the whole induction principle, while [n] becomes an argument to the proposition [P]. *) (* THOUGHT EXERCISE (not to be handed in): Here is a variant of the set [tree] defined above. For each natural number [n], the type [htree n] describes balanced trees of height exactly [n]. Write out the induction principle for [htree] and compare it with what Coq generates. *) Inductive htree (X:Set) : nat->Set := | hleaf : nat -> htree X zero | hnode : forall n:nat, htree X n -> htree X n -> htree X (S n). Check htree_ind. (* So far, the whole discussion applies to inductively defined SETS. Inductively defined PROPOSITIONS are handled a little differently. For example, from what we've said so far, you might expect the inductive definition of [evenI] Inductive evenI : nat -> Prop := | even_zero : evenI O | even_SS : forall n:nat, evenI n -> evenI (S (S n)). to give rise to an induction principle that looks like this... evenI_ind_max : forall P : (forall n : nat, evenI n -> Prop), P O even_zero -> (forall (n : nat) (e : evenI n), P n e -> P (S (S n)) (even_SS n e)) -> forall (n : nat) (e : evenI n), P n e ... because: - Since [evenI] is indexed by a number [n] (every [evenI] object [e] is a piece of evidence that some particular number [n] is even), the proposition [P] is parameterized by both [n] and [e] -- that is, the induction principle can be used to prove assertions involving both an even number and the evidence that it is even. - Since there are two ways of giving evidence of evenness ([evenI] has two constructors), applying the induction principle generates two subgoals: - We must prove that [P] holds for [O] and [even_zero]. - We must prove that, whenever [n] is an even number and [e] is evidence of its evenness, if [P] holds of [n] and [e], then it also holds of [S (S n)] and [even_SS n e]. - If these subgoals can be proved, then the induction principle tells us that [P] is true for ALL even numbers [n] and evidence [e] of their evenness. But this is a little more flexibility than we actually need or want: it is giving us a way to prove logical assertions about EVIDENCE of evenness, while all we really care about is proving properties of numbers that are even -- we interested in assertions about numbers, not about evidence. It would therefore be more convenient to have an induction principle for proving propositions [P] that are parameterized just by [n] and whose conclusion establishes [P] for all even numbers [n]: forall P : nat -> Prop, ... -> forall n : nat, evenI n -> P n For this reason, Coq actually generates the following simplified induction principle for [evenI]: evenI_ind : forall P : nat -> Prop, P O -> (forall n : nat, evenI n -> P n -> P (S (S n))) -> forall n : nat, evenI n -> P n Similarly, from the inductive definition of the proposition [and A B] Inductive and (A B : Prop) : Prop := conj : A -> B -> (and A B). we might expect Coq to generate this induction principle and_ind_max : forall (A B : Prop) (P : A /\ B -> Prop), (forall (a : A) (b : B), P (conj A B a b)) -> forall a : A /\ B, P a but actually it generates this simpler and more useful one: and_ind : forall A B P : Prop, (A -> B -> P) -> A /\ B -> P In the same way, when given the inductive definition of [or A B] Inductive or (A B : Prop) : Prop := | or_introl : A -> or A B | or_intror : B -> or A B. instead of the "maximal induction principle" or_ind_max : forall (A B : Prop) (P : A \/ B -> Prop), (forall a : A, P (or_introl A B a)) -> (forall b : B, P (or_intror A B b)) -> forall o : A \/ B, P o what Coq actually generates is this: or_ind : forall A B P : Prop, (A -> P) -> (B -> P) -> A \/ B -> P *) (* ---------------------------------------------------------------------- *) (* Relations as propositions *) (* A proposition parameterized over a number (like [evenI]) can be thought of as a PREDICATE -- i.e., the subset of [nat] for which the proposition is provable. A two-argument proposition can be thought of as a RELATION -- i.e., the set of pairs for which the proposition is provable. *) (* For example, here is the "less than or equal" relation on numbers. *) Inductive le (n:nat) : nat -> Prop := | le_n : le n n | le_S : forall m:nat, (le n m) -> (le n (S m)). Notation "m <= n" := (le m n). (* Note that the left-hand argument [n] to [le] is the same everywhere in the definition, so we've made it a general parameter. This leads to a simpler induction principle. *) (* THOUGHT EXERCISE: Print out the induction principle for [le] and check that you understand it. Then try redefining [le] with [n] as an index rather than a general parameter (i.e., write the first line as [Inductive le : nat -> nat -> Prop]) and check how the generated induction principle changes. *) (* Some sanity checks on the definition. (Notice that, although these are the same kind of simple "unit tests" as we gave for the testing functions we wrote in the first few lectures, we must construct their proofs explicitly -- [simpl] doesn't do the job for us. *) Lemma check_le1 : three <= three. Proof. apply le_n. Qed. Lemma check_le2 : three <= six. Proof. apply le_S. apply le_S. apply le_S. apply le_n. Qed. Lemma check_le3 : ~ (two <= one). Proof. intros H. inversion H. inversion H1. Qed. (* The "strictly less than" relation [n < m] can now be defined on top of [le]. *) Definition lt (n m:nat) := le (S n) m. Notation "m < n" := (lt m n). (* Here are a few more simple relations on numbers *) Inductive square_of : nat -> nat -> Prop := sq : forall n:nat, square_of n (times n n). Inductive next_nat (n:nat) : nat -> Prop := | nn : next_nat n (S n). Inductive next_even (n:nat) : nat -> Prop := | ne_1 : evenI (S n) -> next_even n (S n) | ne_2 : evenI (S (S n)) -> next_even n (S (S n)). (* EXERCISE: define an inductive relation [total_relation] that holds between every pair of natural numbers. *) (* FILL IN HERE *) (* EXERCISE: define an inductive relation [empty_relation] (on numbers) that never holds. *) (* FILL IN HERE *) (* ---------------------------------------------------------------------- *) (* Relations, in general *) (* We've now defined a few particular relations. As you may remember from an undergraduate discrete math course, there are a lot of ways of discussing and describing relations *in general* -- ways of classifying relations (are they reflexive, transitive, etc.), theorems that can be proved generically about classes of relations, constructions that build one relation from another, etc. Let us pause here to review a few of these that will be useful in what follows. *) (* A RELATION on a set [X] is a proposition parameterized by two [X]s -- i.e., it is a logical assertion involving two values from the set [X]. *) Definition relation (X: Set) := X->X->Prop. (* A relation [R] on a set [X] is a PARTIAL FUNCTION if, for every [x : X], there is at most one [y] such that [R x y] -- or, to put it differently, if [R x y1] and [R x y2] together imply [y1 = y2]. *) Definition partial_function (X: Set) (R: relation X) := forall x y1 y2 : X, R x y1 -> R x y2 -> y1 = y2. Lemma le_not_a_partial_function : ~ (partial_function _ le). Proof. unfold not. unfold partial_function. intros H. assert (zero <= one) as A01. Case "Proof of assertion". apply le_S. apply le_n. assert (zero <= two) as A02. Case "Proof of assertion". apply le_S. apply le_S. apply le_n. assert (one = two) as Nonsense. Case "Proof of assertion". apply H with (x:=zero) (y1:=one) (y2:=two). apply A01. apply A02. inversion Nonsense. Qed. (* EXERCISE: Show that the [total_relation] you defined above is not a partial function, but that [empty_relation] is. *) (* FILL IN HERE *) Definition reflexive (X: Set) (R: relation X) := forall a : X, R a a. Lemma le_reflexive : reflexive _ le. Proof. unfold reflexive. intros n. apply le_n. Qed. Definition transitive (X: Set) (R: relation X) := forall a b c : X, (R a b) -> (R b c) -> (R a c). Lemma le_transitive : transitive _ le. Proof. intros m n o Hmn Hno. induction Hno. apply Hmn. apply le_S. apply IHHno. Qed. Lemma lt_transitive : transitive _ lt. Proof. (* Prove this by induction on evidence that [n] is less than [o] *) unfold lt. unfold transitive. intros m n o Hmn Hno. induction Hno. (* FILL IN HERE (and delete "Admitted") *) Admitted. Lemma lt_transitive' : transitive _ lt. Proof. (* Prove the same thing again by induction on [o] *) unfold lt. unfold transitive. intros m n o Hmn Hno. induction o. (* FILL IN HERE (and delete "Admitted") *) Admitted. (* The transitivity of [le], in turn, can be used to prove a fact ([Sn_not_le_n]) that is needed for the proof of symmetry below... *) Lemma le_S_le : forall n m, S n <= m -> n <= m. Proof. intros n m H. apply le_transitive with (b := S n). apply le_S. apply le_n. apply H. Qed. Lemma Sn_le_Sm__n_le_m : forall n m, (S n <= S m) -> (n <= m). Proof. intros n m H. inversion H. apply le_n. apply le_S_le. apply H1. Qed. Lemma Sn_not_le_n : forall n, ~ (S n <= n). Proof. induction n. Case "O". intros H. inversion H. Case "S". intros H. unfold not in IHn. apply IHn. apply Sn_le_Sm__n_le_m. apply H. Qed. Definition symmetric (X: Set) (R: relation X) := forall a b : X, (R a b) -> (R b a). Lemma le_not_symmetric : ~ (symmetric _ le). Proof. (* OPTIONAL EXERCISE *) Admitted. Definition antisymmetric (X: Set) (R: relation X) := forall a b : X, (R a b) -> (R b a) -> a = b. Lemma le_antisymmetric : antisymmetric _ le. Proof. (* Proof sketch: Suppose that n <= m and m <= n, and consider the constructors used to prove these facts. If either constructor is le_n, the result follows easily. If both are le_S, then for some n0 and m0 we have n = S n0 and m = S m0, with n <= m0 and m <= n0. By transitivity of le, it follows that S m0 <= m0. But this contradicts Sn_not_le_n. (Coq hint: If you find yourself stuck at the point where you want to use [Sn_not_le_n] to obtain a contradiction because the [apply] tactic doesn't work when you think it should, try using an [assert] to introduce the specific case of [Sn_not_le_n] that you need.) *) (* OPTIONAL EXERCISE *) Admitted. Definition equivalence (X:Set) (R: relation X) := (reflexive _ R) /\ (symmetric _ R) /\ (transitive _ R). Definition partial_order (X:Set) (R: relation X) := (reflexive _ R) /\ (antisymmetric _ R) /\ (transitive _ R). Definition preorder (X:Set) (R: relation X) := (reflexive _ R) /\ (transitive _ R). Lemma le_preorder : preorder _ le. Proof. unfold preorder. apply conj. Case "refl". apply le_reflexive. Case "trans". apply le_transitive. Qed. Inductive refl_trans_closure (X:Set) (R: relation X) : X -> X -> Prop := | rtc_R : forall (x y : X), R x y -> refl_trans_closure X R x y | rtc_refl : forall (x : X), refl_trans_closure X R x x | rtc_trans : forall (x y z : X), refl_trans_closure X R x y -> refl_trans_closure X R y z -> refl_trans_closure X R x z. Definition preserves (X: Set) (R: relation X) (P: X->Prop) := forall (x y : X), P x -> R x y -> P y. Lemma rtc_preserves : forall (X: Set) (R: relation X) (P: X->Prop), preserves _ R P -> preserves _ (refl_trans_closure _ R) P. Proof. (* OPTIONAL EXERCISE *) Admitted. (* ====================================================================== *) (* LECTURE 9 *) (* Notes: - The homework assignment for this week is not completely set (because, like last week, I'm not certain how much material I'm going to get through today). The final version of the assignment will be announced by this evening. - Please send me your comments about how you feel the course is going. *) (* ---------------------------------------------------------------------- *) (* Operational semantics *) (* We've defined a lot of relations on numbers. Let's now turn our attention to something closer to the heart of this course: relations on PROGRAMS. *) Module SimpleArithEval. (* In lecture 5, we defined an extremely simple programming language... *) Inductive tm : Set := | tm_const : nat -> tm | tm_plus : tm -> tm -> tm. (* ... and an INTERPRETER that maps each term to a number. *) Fixpoint interp (t:tm) {struct t} : nat := match t with | tm_const n => n | tm_plus t1 t2 => plus (interp t1) (interp t2) end. (* We then went on to define several program transformations and show that they preserved the meaning of programs as defined by [interp]. The last of these transformations was a "single-step evaluator" that finds the leftmost [tm_plus] whose immediate children are both constants and replaces it by a [tm_const]. The same notion of single-step evaluation can be expressed as an EVALUATION RELATION on terms: *) Inductive eval : tm -> tm -> Prop := | E_PlusConstConst : forall n1 n2, eval (tm_plus (tm_const n1) (tm_const n2)) (tm_const (plus n1 n2)) | E_Plus1 : forall t1 t1' t2, (eval t1 t1') -> eval (tm_plus t1 t2) (tm_plus t1' t2) | E_Plus2 : forall n1 t2 t2', (eval t2 t2') -> eval (tm_plus (tm_const n1) t2) (tm_plus (tm_const n1) t2'). (* THOUGHT EXERCISE: What induction principle is generated for [eval]? *) (* Let's verify that this relation matches the behavior of the old [simplify_step] function in a few test cases. *) (* If [t1] can take a step to [t1'], then [tm_plus t1 t2] steps to [plus t1' t2]: *) Lemma check_eval_2: eval (tm_plus (tm_plus (tm_const zero) (tm_const three)) (tm_plus (tm_const two) (tm_const four))) (tm_plus (tm_const (plus zero three)) (tm_plus (tm_const two) (tm_const four))). Proof. apply E_Plus1. apply E_PlusConstConst. Qed. (* Right-hand sides of sums can take a step only when the left-hand side is finished: if [t2] can take a step to [t2'], then [tm_plus (tm_const n) t2] steps to [tm_plus (tm_const n) t2']: *) Lemma check_simplify_step_3: eval (tm_plus (tm_const zero) (tm_plus (tm_const two) (tm_plus (tm_const zero) (tm_const three)))) (tm_plus (tm_const zero) (tm_plus (tm_const two) (tm_const (plus zero three)))). Proof. (* FILL IN HERE (and delete "Admitted") *) Admitted. (* Proofs of simple facts like this -- term [t] takes a step to become [t'] -- consist of sequences of applications of the three constructors of [eval] that, essentially, construct a data structure -- a tree whose single leaf is labeled E_PlusConstConst and whose internal nodes are labeled either [E_Plus1] or [E_Plus2] -- to serve as evidence for the assertion [eval t t']. We use the term "tree" here even though each internal node has exactly one child (because the constructors [E_Plus1] and [E_Plus2] each take only one argument) so that the same terminology will apply to other inductive definitions where constructors take multiple arguments. These "evidence trees" are commonly called DERIVATIONS. *) (* As we did for [simplify_step] and the other program transformations in Lecture 5, we can check that the [eval] relation preserves the meaning of terms as defined by the [interp] function. *) Theorem eval_preserves_interp : forall t t', eval t t' -> interp t = interp t'. Proof. intros t t' E. induction E. Case "E_PlusConstConst". reflexivity. Case "EPlus1". simpl. rewrite -> IHE. reflexivity. Case "E_Plus2". simpl. rewrite -> IHE. reflexivity. Qed. (* Side note: Formally, [Theorem] means precisely the same as [Lemma]. Calling something a [Theorem] is a signal to the human reader of a proof script that the assertion being proved is a particularly important or interesting one. *) (* OPTIONAL EXERCISE: Is the word "preserves" in the name of this lemma being used in precisely the same sense as we did earlier? That is, can the statement of the lemma be written in terms of the constant [preserves] that we defined above? If yes, do so and give a proof. If no, explain why not. *) (* One interesting property of the [eval] relation is that it is DETERMINISTIC: for each [t], there is at most one [t'] such that [eval t t'] is provable. Formally, this is the same as saying that [eval] is a partial function. *) Theorem eval_deterministic : partial_function _ eval. Proof. (* Proof sketch: We must show that if [x] evaluates to both [y1] and [y2] then [y1] and [y2] are equal. Consider the last constructors used in the derivations of [eval x y1] and [eval x y2]. - If both are [E_PlusConstConst], the result is immediate. - It cannot happen that one is [E_PlusConstConst] and the other is [E_Plus1] or [E_Plus2], since this would imply that [x] has the form [tm_plus t1 t2] where both [t1] and [t2] are constants (by [E_PlusConstConst]) AND one of [t1] or [t2] has the form [tm_plus ...]. - Similarly, it cannot happen that one is [E_Plus1] and the other is [E_Plus2], since this would imply that [x] has the form [tm_plus t1 t2] where [t1] has both the form [tm_plus t1 t2] and the form [tm_const n]. - The cases when both derivations end with [E_Plus1] or [E_Plus2] follow by the induction hypothesis. *) unfold partial_function. intros x y1 y2 Hy1 Hy2. generalize dependent y2. induction Hy1. Case "E_PlusConstConst". intros y2 Hy2. inversion Hy2. Case "E_PlusConstConst". reflexivity. Case "E_Plus1". inversion H2. Case "E_Plus2". inversion H2. Case "E_Plus1". intros y2 Hy2. inversion Hy2. Case "E_PlusConstConst". rewrite <- H0 in Hy1. inversion Hy1. Case "E_Plus1". assert (t1' = t1'0) as eq. Case "Proof of assertion". apply IHHy1. apply H2. rewrite <- eq. reflexivity. Case "E_Plus2". rewrite <- H in Hy1. inversion Hy1. Case "E_Plus2". intros y2 Hy2. inversion Hy2. Case "E_PlusConstConst". rewrite <- H1 in Hy1. inversion Hy1. Case "E_Plus1". inversion H2. Case "E_Plus2". assert (t2' = t2'0) as eq. Case "Proof of assertion". apply IHHy1. apply H2. rewrite <- eq. reflexivity. Qed. End SimpleArithEval. Module SimpleArithEvalAgain. Export SimpleArith. (* Before we move on, let's take a moment to slightly generalize the way we state the definition of single-step evaluation. It is useful to think of the [eval] relation as defining a sort of ABSTRACT MACHINE for evaluating programs: - At any moment, the STATE of the machine is a term. - A STEP of the machine is an atomic unit of computation -- a single "add" operation, in the case of the present tiny programming language. - The FINAL STATES of the machine are ones where there is no more computation to be done. We can then think about "executing" a term [t] as follows: - Take [t] as the starting state of the machine. - Repeatedly use the [eval] relation to find a sequence of machine states such that each evaluates to the next. - When no more evaluation is possible, "read out" the final state of the machine as the result of execution. Intuitively, it is clear that the final states of the machine are always terms of the form [tm_const n] for some [n]. We call such terms VALUES. *) Inductive value : tm -> Prop := v_const : forall n, value (tm_const n). (* Having introduced the idea of VALUES, we can use it in the definition of the [eval] relation to write [E_Plus2] rule in a slightly more intuitive way: *) Inductive eval : tm -> tm -> Prop := | E_PlusConstConst : forall n1 n2, eval (tm_plus (tm_const n1) (tm_const n2)) (tm_const (plus n1 n2)) | E_Plus1 : forall t1 t1' t2, (eval t1 t1') -> eval (tm_plus t1 t2) (tm_plus t1' t2) | E_Plus2 : forall v1 t2 t2', (value v1) (* <----- *) -> (eval t2 t2') -> eval (tm_plus v1 t2) (tm_plus v1 t2'). (* As a sanity check on this change, let's re-verify the properties we proved a few minutes ago... *) Theorem eval_preserves_interp : forall t t', eval t t' -> interp t = interp t'. Proof. (* Like the one for the earlier definition of [eval], this proof is easy. Try to do it without peeking. *) (* FILL IN HERE (and delete "Admitted") *) Admitted. Theorem eval_deterministic : partial_function _ eval. Proof. (* Proof sketch: We must show that if [x] evaluates to both [y1] and [y2] then [y1] and [y2] are equal. Consider the last constructors used in the derivations of [eval x y1] and [eval x y2]. - If both are [E_PlusConstConst], the result is immediate. - It cannot happen that one is [E_PlusConstConst] and the other is [E_Plus1] or [E_Plus2], since this would imply that [x] has the form [tm_plus t1 t2] where both [t1] and [t2] are constants (by [E_PlusConstConst]) AND one of [t1] or [t2] has the form [tm_plus ...]. - Similarly, it cannot happen that one is [E_Plus1] and the other is [E_Plus2], since this would imply that [x] has the form [tm_plus t1 t2] where [t1] both has the form [tm_plus t1 t2] and is a value (hence has the form [tm_const n]). - The cases when both derivations end with [E_Plus1] or [E_Plus2] follow by the induction hypothesis. *) (* Most of this proof is the same as the one above. But to get maximum benefit from the exercise you should try to write it from scratch and just use the earlier one if you get stuck. *) (* FILL IN HERE (and delete "Admitted") *) Admitted. End SimpleArithEvalAgain. (* ---------------------------------------------------------------------- *) (* Existential quantification *) (* We saw a couple of lectures ago how a variety of logical connectives ([and], [or], etc.) can be encoded in Coq as inductively defined propositions. Here is an inductive definition of one more important connective: existential quantification. *) Inductive ex (X : Type) (P : X -> Prop) : Prop := ex_intro : forall witness:X, P witness -> ex X P. (* The intuition is that, in order to give evidence for the assertion "there is some x for which P holds" we must actually name a WITNESS -- a specific value x for which we can give evidence that P holds. *) (* Next we add some convenient notation for the [ex] type. The details of how this works are not important: the critical point is that it allows us to write [exists x, P] or [exists x:t, P], just as we do with the [forall] quantifier. *) Notation "'exists' x , p" := (ex _ (fun x => p)) (at level 200, x ident, right associativity) : type_scope. Notation "'exists' x : t , p" := (ex _ (fun x:t => p)) (at level 200, x ident, right associativity) : type_scope. (* We can use the same set of tactics to manipulate existentials as we have been using all along. For example, if to prove an existential, we [apply] the constructor [ex_intro]. Since the premise of [ex_intro] involves a variable ([witness]) that does not appear in its conclusion, we need to explicitly give its value when we use [apply]. *) Lemma exists_example_1 : exists n, plus n (times n n) = six. Proof. apply ex_intro with (witness := two). reflexivity. Qed. (* On the other hand, if we have an existential hypothesis in the context, we can eliminate it with [inversion]. *) Lemma exists_example_2 : forall m, (exists n, m = plus four n) -> (exists o, m = plus two o). Proof. intros m H. inversion H. apply ex_intro with (witness := plus two witness). apply H0. Qed. Lemma dist_exists_or : forall (X:Set) (P Q : X -> Prop), (exists x, P x \/ Q x) <-> (exists x, P x) \/ (exists x, Q x). Proof. (* FILL IN HERE (and delete "Admitted") *) Admitted. Lemma dist_not_exists : forall (X:Set) (P : X -> Prop), (forall x, P x) -> ~ (exists x, ~ P x). Proof. (* OPTIONAL EXERCISE *) Admitted. Inductive composition (X: Set) (Q R: relation X) : X -> X -> Prop := comp : forall x z, (exists y, Q x y /\ R y z) -> composition X Q R x z. (* ---------------------------------------------------------------------- *) (* Normal forms *) Module SimpleArithEvalContd. Export SimpleArithEvalAgain. (* In this language, every term is either a value or it can "make progress" by evaluating to some other term. *) Theorem eval_progress : forall t, value t \/ (exists t', eval t t'). Proof. (* Proof sketch: By induction on [t]. - If [t] is a constant, then it is a value. - If [t = tm_plus t1 t2], then by the IH [t1] and [t2] are either values or can take steps under [eval]. - If [t1] and [t2] are both values, then [t] can take a step, by [E_PlusConstConst]. - If [t1] is a value and [t2] can take a step, then so can [t], by [E_Plus2]. - If [t1] can take a step, then so can [t], by [E_Plus1]. *) induction t. Case "tm_const". apply or_introl. apply v_const. Case "tm_plus". apply or_intror. inversion IHt1. Case "l". inversion IHt2. Case "l". inversion H. inversion H0. apply ex_intro with (witness := tm_const (plus n n0)). apply E_PlusConstConst. Case "r". inversion H0. apply ex_intro with (witness := tm_plus t1 witness). apply E_Plus2. apply H. apply H1. Case "r". inversion H. apply ex_intro with (witness := tm_plus witness t2). apply E_Plus1. apply H0. Qed. (* This property can be extended to tell us something very interesting about values: they are EXACTLY the terms that cannot make progress in this sense. To state this fact, let's begin by giving a name to terms that cannot make progress: We'll call them NORMAL FORMS. *) Definition normal_form (X:Set) (R:relation X) (t:X) : Prop := ~ exists t', R t t'. (* We've actually defined what it is to be a normal form for an arbitrary relation [R] over an arbitrary set [X], not just for the particular relation of evaluation over terms that we are interested in at the moment. We'll re-use the same terminology for talking about other relations later. *) (* We can use this terminology to generalize the observation we made in Lemma [eval_progress]: normal forms and values are actually the same thing. *) Lemma value_iff_nf : forall t, value t <-> normal_form _ eval t. Proof. (* Proof sketch: The -> direction is straightforward from the definitions. The <- direction is a corollary of [eval_progress]. *) intros t. unfold iff. apply conj. Case "->". intros H. unfold normal_form. intros contra. inversion H. rewrite <- H0 in contra. inversion contra. inversion H1. Case "<-". intros H. unfold normal_form in H. assert (value t \/ exists t', eval t t') as G. Case "Proof of assertion". apply eval_progress. inversion G. Case "l". apply H0. Case "r". unfold not in H. apply H in H0. inversion H0. Qed. (* Why is this an interesting fact? For two reasons: - 1. Because [value] is a syntactic concept -- it is a defined by looking at the form of a term -- while [normal_form] is a semantic one -- it is defined by looking at how the term evaluates. Is it not obvious that these concepts should coincide. - 2. Indeed, there are lots of languages in which the concepts of normal form and value do NOT coincide. Let's examine how this can happen... *) (* We might, for example, accidentally define [value] so that it includes some terms that are not finished evaluating. *) Module Temp1. (* Open an inner module so we can redefine [value] and [eval]. *) Inductive value : tm -> Prop := | v_const : forall n, value (tm_const n) | v_funny : forall t1 n2, value (tm_plus t1 (tm_const n2)). (* <---- *) Inductive eval : tm -> tm -> Prop := | E_PlusConstConst : forall n1 n2, eval (tm_plus (tm_const n1) (tm_const n2)) (tm_const (plus n1 n2)) | E_Plus1 : forall t1 t1' t2, (eval t1 t1') -> eval (tm_plus t1 t2) (tm_plus t1' t2) | E_Plus2 : forall v1 t2 t2', (value v1) -> (eval t2 t2') -> eval (tm_plus v1 t2) (tm_plus v1 t2'). Lemma value_not_same_as_normal_form : exists t, value t /\ ~ normal_form _ eval t. Proof. (* FILL IN HERE (and delete "Admitted") *) Admitted. End Temp1. (* Alternatively, we might accidentally define [eval] so that it permits something designated as a value to evaluate further. *) Module Temp2. Inductive value : tm -> Prop := | v_const : forall n, value (tm_const n). Inductive eval : tm -> tm -> Prop := | E_Funny : forall n, (* <---- *) eval (tm_const n) (tm_plus (tm_const n) (tm_const zero)) | E_PlusConstConst : forall n1 n2, eval (tm_plus (tm_const n1) (tm_const n2)) (tm_const (plus n1 n2)) | E_Plus1 : forall t1 t1' t2, (eval t1 t1') -> eval (tm_plus t1 t2) (tm_plus t1' t2) | E_Plus2 : forall v1 t2 t2', (value v1) -> (eval t2 t2') -> eval (tm_plus v1 t2) (tm_plus v1 t2'). Lemma value_not_same_as_normal_form : exists t, value t /\ ~ normal_form _ eval t. Proof. (* FILL IN HERE (and delete "Admitted") *) Admitted. End Temp2. (* Finally, we might accidentally define [value] and [eval] so that there is some term that is not a value but that cannot take a step in the [eval] relation. Such terms are said to be STUCK. *) Module Temp3. Inductive value : tm -> Prop := | v_const : forall n, value (tm_const n). Inductive eval : tm -> tm -> Prop := | E_PlusConstConst : forall n1 n2, eval (tm_plus (tm_const n1) (tm_const n2)) (tm_const (plus n1 n2)) | E_Plus1 : forall t1 t1' t2, (eval t1 t1') -> eval (tm_plus t1 t2) (tm_plus t1' t2). (* <---- note that E_Plus2 is missing *) Lemma value_not_same_as_normal_form : exists t, ~ value t /\ normal_form _ eval t. Proof. (* FILL IN HERE (and delete "Admitted") *) Admitted. End Temp3. End SimpleArithEvalContd. (* Copy definition of [normal_form] into the top-level environment so we can refer to it later. *) Notation normal_form := SimpleArithEvalContd.normal_form.