(* A COMPUTATIONAL VIEW OF PROOFS ============================== We've seen and used many specific features of Coq. Today's notes paint a "big picture" that puts these specifics in context. The material presented here will not, as such, appear on Wednesday's exam, but I hope it will help you make connections and clarify the material we've already covered. COQ'S TWO UNIVERSES ------------------- Coq divides the world into two distinct universes: - [Set] is the universe of COMPUTATIONS and DATA - [Prop] is the universe of LOGICAL ASSERTIONS and EVIDENCE The two universes have some deep similarities -- in each, we can talk about values, inductive definitions, quantification, etc. -- but there are some technical differences; more importantly, they play quite different roles in the way we define and reason about mathematical structures. (Technically, there is another universe called [Type] that subsumes both [Set] and [Prop]. But we don't need to think about this one for the moment.) VALUES ------ Both universes begin with an infinite set of CONSTRUCTORS. Constructors have no internal structure: they are just "tags", and all we can do with them is tell whether they are the same or different. Examples: - [yes], [no], [O], [S], [nil], [cons], [even_zero], [even_SS], [E_PlusConstConst], ... The simplest VALUES are trees of constructor applications. Their leaves are nullary constructors (applied to no arguments), and internal nodes are applications of constructors to one or more values. Examples: - [yes] - [O] - [S (S (S O))] - [even_zero] - [even_SS (S (S O)) even_zero] - [even_SS (S (S (S (S O)))) (even_SS (S (S O)) even_zero)] In the universe [Set], we think of values as DATA (belonging to certain sets). In [Prop], we think of values as EVIDENCE (that prove certain propositions). Values consisting entirely of constructor applications can be thought of as TREES. In [Prop], such trees are called DERIVATION TREES. [Inductive] declarations give names to subsets of the set of all values. For example, the declaration of the inductive type [nat] defines a SET whose ELEMENTS are values representing natural numbers. That is, it picks out a subset [nat] of the set of all values that satisfies the following properties: - the value [O] is in this set - the set is CLOSED under applications of [S] (i.e., if a value [n] is in the set, then [S n] is too) - it is the smallest set satisfying these conditions (i.e., the only values in [nat] are the ones that MUST be, according to the previous two conditions... there is no other "junk"). Sets themselves are also values and can appear as arguments to constructors in compound values. Examples: - [nat] - [nil nat] - [cons nat O (cons nat (S O) (nil nat))] Also, we can write functions that take sets as arguments and return sets as results. For example, [list] is a function of type [Set->Set]: it takes a set [X] as argument and returns as result the set [list X] (whose members are lists with elements drawn from [X]). Similarly, the declaration of the inductive type [evenI] defines a FAMILY OF SETS whose ELEMENTS are values representing evidence that numbers are even. That is, for each [n], the definition picks out a subset [evenI n] of the set of all values satisfying the following properties: - the value [even_zero] is in the set [evenI O] - the sets are CLOSED under well-typed applications of [even_SS] -- i.e., if [e] is in the set [evenI n], then [even_SS n e] is in the set [evenI (S (S n))] - it is the smallest family of sets satisfying these conditions (i.e., the only values in any set [evenI n] are the ones that MUST be, according to the previous two conditions... there is no other junk). EXAMPLE: The [square_of] Relation --------------------------------- Recall the inductive definition of the [square_of] relation: Inductive square_of : nat -> nat -> Prop := sq : forall n:nat, square_of n (times n n). This definition can be read as follows: - We are defining (inductively) a family of sets of values (in [Prop]) that we are going to consider as evidence for propositions of the form [square_of n m] -- that is "[m] is the square of [n]." - For every number [n], there is one way to construct evidence for [square_of n (times n n)]: by applying the constructor [sq] to [n] and [times n n]. That is, whenever [m] is the square of [n], we have a way of constructing evidence of [square_of n m]. - For [m] and [n] where [m] is NOT the square of [n], we have no way of building elements of the type [square_of n m]. FUNCTIONS AND QUANTIFIERS ------------------------- The types [A->B] and [forall x:A, B] both describe functions from [A] to [B]. The only difference is that, in the second case, the expression [B] -- the type of the result -- can mention the argument [x] by name. For example: - The function [fun x:nat => plus x x] has type [nat->nat] -- that is, it maps each number [n] to a number. - The function [fun X:Set => nil (list X)] has type [forall X:Set, list (list X)] -- that is, it maps each set [X] to a particular list of lists of [X]s. In fact, the two ways of writing function types are really the same: In Coq, [A->B] is actually just an abbreviation for [forall x:A, B], where [x] is some variable name not occurring in [B]. For example, the type of [fun x:nat => plus x x] can be written, if we like, as [forall x:nat, nat]. FUNCTIONS AND IMPLICATIONS -------------------------- In both [Set] and [Prop], we can write functions that transform values into other values. Also, functions themselves are values; this means we can - write higher-order functions that take functions as arguments or return functions as results, and - apply constructors to functions to build complex values containing functions. A function of type [P->Q] in [Prop] is something that takes evidence for [P] as an argument and yields evidence for [Q] as its result. Such a function can be regarded as EVIDENCE that [P] implies [Q], since, whenever we have evidence that [P] is true, we can apply the function and get back evidence that [Q] is true: evidence for an implication is a function on evidence. This is why we use the same notation for functions and logical implications in Coq: they are exactly the same thing! This coincidence between functions and implications is an instance of a deep connection between programming languages and mathematical logic known as the CURRY-HOWARD CORRESPONDENCE. EXAMPLE: The Induction Principle for [eval] ------------------------------------------- We can view [eval_ind], the induction principle for the [eval] relation, as a FUNCTION for constructing evidence: it takes four arguments (a relation [P] on terms and three pieces of evidence -- let's call them [E1], [E2], and [E3] -- for the three constructors of the [eval] relation) and it returns evidence (let's call it [E]) proving that, whenever terms [t] and [t0] are related by [eval], they are related by [P]. eval_ind : forall P : tm -> tm -> Prop, (forall n1 n2 : nat, (* <-- E1 *) P (tm_plus (tm_const n1) (tm_const n2)) (tm_const (plus n1 n2))) -> (forall t1 t1' t2 : tm, (* <-- E2 *) eval t1 t1' -> P t1 t1' -> P (tm_plus t1 t2) (tm_plus t1' t2)) -> (forall v1 t2 t2' : tm, (* <-- E3 *) value v1 -> eval t2 t2' -> P t2 t2' -> P (tm_plus v1 t2) (tm_plus v1 t2')) -> forall t t' : tm, (* --> E *) eval t t' -> P t t' Notice that [E] itself is a function. Given [t], [t'], and evidence that [t] evaluates to [t'] (i.e., given a structure belonging to the inductively defined type [eval t t']), it must construct evidence of [P t t'], using [E1], [E2], and [E3] as resources. How does it do this? From the definition of [eval], we know that evidence for [eval t t'] can have three possible forms -- i.e., it consists of an application of one of the three constructors to appropriate arguments. - In the case of the first constructor, the form of the definition tells us that [t = tm_plus (tm_const n1) (tm_const n2)] and [t' = (tm_const (plus n1 n2))], for some numbers [n1] and [n2]. But the evidence [E1] that we passed to [eval_ind] tells us exactly that [P] holds for ALL [t] and [t'] of this form. - In the case of the second constructor, the form of the definition tells us that [t = tm_plus t1 t2] and [t' = tm_plus t1' t2], for some terms [t1], [t1'], and [t2], and also that [eval t1 t1']. Since [t1] and [t1'] are smaller than [t] and [t'], [E] can begin by CALLING ITSELF RECURSIVELY with arguments [t1], [t1'], and the evidence that [t1] evaluates to [t1']. The result of this recursive call will be evidence that [P] holds between [t1] and [t1']. But now [E] can use the evidence [E2] that we passed to [eval_ind]: it can apply [E2] to [t1], [t1'], [t2], the evidence that [t1] reduces to [t1'] and the evidence (which it just got back from the recursive call) of [P t1 t1']. The result of [E2] will then be evidence for [P t t'], which [E] can return. - The case of the third constructor is similar. To summarize: [eval_ind] can be thought of as a FUNCTION that, when given a relation [P] and some appropriate building blocks, returns a FUNCTION for transforming evidence of [eval t t'] into evidence of [P t t']. Internally, this is done by recursive pattern matching, using as building blocks the "case proofs" ([E1], [E2], and [E3]) that we supply to [eval_ind]. HINTS FOR WEDNESDAY =================== Finally, some suggestions to help focus your studying for the exam. - There will be questions involving calculating induction principles from inductive definitions and vice versa. However, they will all be in [Set]: you will not need to worry about the differences between "maximal induction principles" and "simplified induction principles" in [Prop]. My best recommendation for preparing for these questions is to begin by MEMORIZING a few typical inductive definitions ([nat], [list], [htree], etc.) and the corresponding induction principles. *) Require Export lec0809.