Mercurial > cpdt > repo
comparison src/DataStruct.v @ 426:5f25705a10ea
Pass through DataStruct, to incorporate new coqdoc features; globally replace [refl_equal] with [eq_refl]
author | Adam Chlipala <adam@chlipala.net> |
---|---|
date | Wed, 25 Jul 2012 18:10:26 -0400 |
parents | 539ed97750bb |
children | 8077352044b2 |
comparison
equal
deleted
inserted
replaced
425:6ed5ee4845e4 | 426:5f25705a10ea |
---|---|
30 | 30 |
31 Inductive ilist : nat -> Set := | 31 Inductive ilist : nat -> Set := |
32 | Nil : ilist O | 32 | Nil : ilist O |
33 | Cons : forall n, A -> ilist n -> ilist (S n). | 33 | Cons : forall n, A -> ilist n -> ilist (S n). |
34 | 34 |
35 (** We might like to have a certified function for selecting an element of an [ilist] by position. We could do this using subset types and explicit manipulation of proofs, but dependent types let us do it more directly. It is helpful to define a type family %\index{Gallina terms!fin}%[fin], where [fin n] is isomorphic to [{m : nat | m < n}]. The type family name stands for %``%#"#finite.#"#%''% *) | 35 (** We might like to have a certified function for selecting an element of an [ilist] by position. We could do this using subset types and explicit manipulation of proofs, but dependent types let us do it more directly. It is helpful to define a type family %\index{Gallina terms!fin}%[fin], where [fin n] is isomorphic to [{m : nat | m < n}]. The type family name stands for "finite." *) |
36 | 36 |
37 (* EX: Define a function [get] for extracting an [ilist] element by position. *) | 37 (* EX: Define a function [get] for extracting an [ilist] element by position. *) |
38 | 38 |
39 (* begin thide *) | 39 (* begin thide *) |
40 Inductive fin : nat -> Set := | 40 Inductive fin : nat -> Set := |
158 : nat | 158 : nat |
159 ]] | 159 ]] |
160 *) | 160 *) |
161 (* end thide *) | 161 (* end thide *) |
162 | 162 |
163 (* begin hide *) | |
164 Definition map' := map. | |
165 (* end hide *) | |
166 | |
163 (** Our [get] function is also quite easy to reason about. We show how with a short example about an analogue to the list [map] function. *) | 167 (** Our [get] function is also quite easy to reason about. We show how with a short example about an analogue to the list [map] function. *) |
164 | 168 |
165 Section ilist_map. | 169 Section ilist_map. |
166 Variables A B : Set. | 170 Variables A B : Set. |
167 Variable f : A -> B. | 171 Variable f : A -> B. |
170 match ls with | 174 match ls with |
171 | Nil => Nil | 175 | Nil => Nil |
172 | Cons _ x ls' => Cons (f x) (imap ls') | 176 | Cons _ x ls' => Cons (f x) (imap ls') |
173 end. | 177 end. |
174 | 178 |
175 (** It is easy to prove that [get] %``%#"#distributes over#"#%''% [imap] calls. *) | 179 (** It is easy to prove that [get] "distributes over" [imap] calls. *) |
176 | 180 |
177 (* EX: Prove that [get] distributes over [imap]. *) | 181 (* EX: Prove that [get] distributes over [imap]. *) |
178 | 182 |
179 (* begin thide *) | 183 (* begin thide *) |
180 Theorem get_imap : forall n (idx : fin n) (ls : ilist A n), | 184 Theorem get_imap : forall n (idx : fin n) (ls : ilist A n), |
186 | 190 |
187 (** The only tricky bit is remembering to use our [dep_destruct] tactic in place of plain [destruct] when faced with a baffling tactic error message. *) | 191 (** The only tricky bit is remembering to use our [dep_destruct] tactic in place of plain [destruct] when faced with a baffling tactic error message. *) |
188 | 192 |
189 (** * Heterogeneous Lists *) | 193 (** * Heterogeneous Lists *) |
190 | 194 |
191 (** Programmers who move to statically typed functional languages from scripting languages often complain about the requirement that every element of a list have the same type. With fancy type systems, we can partially lift this requirement. We can index a list type with a %``%#"#type-level#"#%''% list that explains what type each element of the list should have. This has been done in a variety of ways in Haskell using type classes, and we can do it much more cleanly and directly in Coq. *) | 195 (** Programmers who move to statically typed functional languages from scripting languages often complain about the requirement that every element of a list have the same type. With fancy type systems, we can partially lift this requirement. We can index a list type with a "type-level" list that explains what type each element of the list should have. This has been done in a variety of ways in Haskell using type classes, and we can do it much more cleanly and directly in Coq. *) |
192 | 196 |
193 Section hlist. | 197 Section hlist. |
194 Variable A : Type. | 198 Variable A : Type. |
195 Variable B : A -> Type. | 199 Variable B : A -> Type. |
196 | 200 |
213 | 217 |
214 Inductive member : list A -> Type := | 218 Inductive member : list A -> Type := |
215 | MFirst : forall ls, member (elm :: ls) | 219 | MFirst : forall ls, member (elm :: ls) |
216 | MNext : forall x ls, member ls -> member (x :: ls). | 220 | MNext : forall x ls, member ls -> member (x :: ls). |
217 | 221 |
218 (** Because the element [elm] that we are %``%#"#searching for#"#%''% in a list does not change across the constructors of [member], we simplify our definitions by making [elm] a local variable. In the definition of [member], we say that [elm] is found in any list that begins with [elm], and, if removing the first element of a list leaves [elm] present, then [elm] is present in the original list, too. The form looks much like a predicate for list membership, but we purposely define [member] in [Type] so that we may decompose its values to guide computations. | 222 (** Because the element [elm] that we are "searching for" in a list does not change across the constructors of [member], we simplify our definitions by making [elm] a local variable. In the definition of [member], we say that [elm] is found in any list that begins with [elm], and, if removing the first element of a list leaves [elm] present, then [elm] is present in the original list, too. The form looks much like a predicate for list membership, but we purposely define [member] in [Type] so that we may decompose its values to guide computations. |
219 | 223 |
220 We can use [member] to adapt our definition of [get] to [hlist]s. The same basic [match] tricks apply. In the [MCons] case, we form a two-element convoy, passing both the data element [x] and the recursor for the sublist [mls'] to the result of the inner [match]. We did not need to do that in [get]'s definition because the types of list elements were not dependent there. *) | 224 We can use [member] to adapt our definition of [get] to [hlist]s. The same basic [match] tricks apply. In the [MCons] case, we form a two-element convoy, passing both the data element [x] and the recursor for the sublist [mls'] to the result of the inner [match]. We did not need to do that in [get]'s definition because the types of list elements were not dependent there. *) |
221 | 225 |
222 Fixpoint hget ls (mls : hlist ls) : member ls -> B elm := | 226 Fixpoint hget ls (mls : hlist ls) : member ls -> B elm := |
223 match mls with | 227 match mls with |
368 (** We are starting to develop the tools behind dependent typing's amazing advantage over alternative approaches in several important areas. Here, we have implemented complete syntax, typing rules, and evaluation semantics for simply typed lambda calculus without even needing to define a syntactic substitution operation. We did it all without a single line of proof, and our implementation is manifestly executable. Other, more common approaches to language formalization often state and prove explicit theorems about type safety of languages. In the above example, we got type safety, termination, and other meta-theorems for free, by reduction to CIC, which we know has those properties. *) | 372 (** We are starting to develop the tools behind dependent typing's amazing advantage over alternative approaches in several important areas. Here, we have implemented complete syntax, typing rules, and evaluation semantics for simply typed lambda calculus without even needing to define a syntactic substitution operation. We did it all without a single line of proof, and our implementation is manifestly executable. Other, more common approaches to language formalization often state and prove explicit theorems about type safety of languages. In the above example, we got type safety, termination, and other meta-theorems for free, by reduction to CIC, which we know has those properties. *) |
369 | 373 |
370 | 374 |
371 (** * Recursive Type Definitions *) | 375 (** * Recursive Type Definitions *) |
372 | 376 |
373 (** %\index{recursive type definition}%There is another style of datatype definition that leads to much simpler definitions of the [get] and [hget] definitions above. Because Coq supports %``%#"#type-level computation,#"#%''% we can redo our inductive definitions as _recursive_ definitions. *) | 377 (** %\index{recursive type definition}%There is another style of datatype definition that leads to much simpler definitions of the [get] and [hget] definitions above. Because Coq supports "type-level computation," we can redo our inductive definitions as _recursive_ definitions. *) |
374 | 378 |
375 (* EX: Come up with an alternate [ilist] definition that makes it easier to write [get]. *) | 379 (* EX: Come up with an alternate [ilist] definition that makes it easier to write [get]. *) |
376 | 380 |
377 Section filist. | 381 Section filist. |
378 Variable A : Set. | 382 Variable A : Set. |
432 match ls with | 436 match ls with |
433 | nil => Empty_set | 437 | nil => Empty_set |
434 | x :: ls' => (x = elm) + fmember ls' | 438 | x :: ls' => (x = elm) + fmember ls' |
435 end%type. | 439 end%type. |
436 | 440 |
437 (** The definition of [fmember] follows the definition of [ffin]. Empty lists have no members, and member types for nonempty lists are built by adding one new option to the type of members of the list tail. While for [index] we needed no new information associated with the option that we add, here we need to know that the head of the list equals the element we are searching for. We express that with a sum type whose left branch is the appropriate equality proposition. Since we define [fmember] to live in [Type], we can insert [Prop] types as needed, because [Prop] is a subtype of [Type]. | 441 (** The definition of [fmember] follows the definition of [ffin]. Empty lists have no members, and member types for nonempty lists are built by adding one new option to the type of members of the list tail. While for [ffin] we needed no new information associated with the option that we add, here we need to know that the head of the list equals the element we are searching for. We express that with a sum type whose left branch is the appropriate equality proposition. Since we define [fmember] to live in [Type], we can insert [Prop] types as needed, because [Prop] is a subtype of [Type]. |
438 | 442 |
439 We know all of the tricks needed to write a first attempt at a [get] function for [fhlist]s. | 443 We know all of the tricks needed to write a first attempt at a [get] function for [fhlist]s. |
440 | 444 |
441 [[ | 445 [[ |
442 Fixpoint fhget (ls : list A) : fhlist ls -> fmember ls -> B elm := | 446 Fixpoint fhget (ls : list A) : fhlist ls -> fmember ls -> B elm := |
457 match ls with | 461 match ls with |
458 | nil => fun _ idx => match idx with end | 462 | nil => fun _ idx => match idx with end |
459 | _ :: ls' => fun mls idx => | 463 | _ :: ls' => fun mls idx => |
460 match idx with | 464 match idx with |
461 | inl pf => match pf with | 465 | inl pf => match pf with |
462 | refl_equal => fst mls | 466 | eq_refl => fst mls |
463 end | 467 end |
464 | inr idx' => fhget ls' (snd mls) idx' | 468 | inr idx' => fhget ls' (snd mls) idx' |
465 end | 469 end |
466 end. | 470 end. |
467 | 471 |
468 (** By pattern-matching on the equality proof [pf], we make that equality known to the type-checker. Exactly why this works can be seen by studying the definition of equality. *) | 472 (** By pattern-matching on the equality proof [pf], we make that equality known to the type-checker. Exactly why this works can be seen by studying the definition of equality. *) |
469 | 473 |
474 (* begin hide *) | |
475 Definition foo := (@eq, @eq_refl). | |
476 (* end hide *) | |
477 | |
470 Print eq. | 478 Print eq. |
471 (** %\vspace{-.15in}% [[ | 479 (** %\vspace{-.15in}% [[ |
472 Inductive eq (A : Type) (x : A) : A -> Prop := refl_equal : x = x | 480 Inductive eq (A : Type) (x : A) : A -> Prop := eq_refl : x = x |
473 | 481 |
474 ]] | 482 ]] |
475 | 483 |
476 In a proposition [x = y], we see that [x] is a parameter and [y] is a regular argument. The type of the constructor [refl_equal] shows that [y] can only ever be instantiated to [x]. Thus, within a pattern-match with [refl_equal], occurrences of [y] can be replaced with occurrences of [x] for typing purposes. *) | 484 In a proposition [x = y], we see that [x] is a parameter and [y] is a regular argument. The type of the constructor [eq_refl] shows that [y] can only ever be instantiated to [x]. Thus, within a pattern-match with [eq_refl], occurrences of [y] can be replaced with occurrences of [x] for typing purposes. *) |
477 (* end thide *) | 485 (* end thide *) |
478 | 486 |
479 End fhlist. | 487 End fhlist. |
480 | 488 |
481 Implicit Arguments fhget [A B elm ls]. | 489 Implicit Arguments fhget [A B elm ls]. |
636 | 644 |
637 (** Even if Coq would generate complete induction principles automatically for nested inductive definitions like the one we started with, there would still be advantages to using this style of reflexive encoding. We see one of those advantages in the definition of [inc], where we did not need to use any kind of auxiliary function. In general, reflexive encodings often admit direct implementations of operations that would require recursion if performed with more traditional inductive data structures. *) | 645 (** Even if Coq would generate complete induction principles automatically for nested inductive definitions like the one we started with, there would still be advantages to using this style of reflexive encoding. We see one of those advantages in the definition of [inc], where we did not need to use any kind of auxiliary function. In general, reflexive encodings often admit direct implementations of operations that would require recursion if performed with more traditional inductive data structures. *) |
638 | 646 |
639 (** ** Another Interpreter Example *) | 647 (** ** Another Interpreter Example *) |
640 | 648 |
641 (** We develop another example of variable-arity constructors, in the form of optimization of a small expression language with a construct like Scheme's %\texttt{%#<tt>#cond#</tt>#%}%. Each of our conditional expressions takes a list of pairs of boolean tests and bodies. The value of the conditional comes from the body of the first test in the list to evaluate to [true]. To simplify the %\index{interpreters}%interpreter we will write, we force each conditional to include a final, default case. *) | 649 (** We develop another example of variable-arity constructors, in the form of optimization of a small expression language with a construct like Scheme's <<cond>>. Each of our conditional expressions takes a list of pairs of boolean tests and bodies. The value of the conditional comes from the body of the first test in the list to evaluate to [true]. To simplify the %\index{interpreters}%interpreter we will write, we force each conditional to include a final, default case. *) |
642 | 650 |
643 Inductive type' : Type := Nat | Bool. | 651 Inductive type' : Type := Nat | Bool. |
644 | 652 |
645 Inductive exp' : type' -> Type := | 653 Inductive exp' : type' -> Type := |
646 | NConst : nat -> exp' Nat | 654 | NConst : nat -> exp' Nat |
825 match goal with | 833 match goal with |
826 | [ |- context[if ?E then _ else _] ] => destruct E | 834 | [ |- context[if ?E then _ else _] ] => destruct E |
827 end; crush. | 835 end; crush. |
828 Qed. | 836 Qed. |
829 | 837 |
830 (** Now the final theorem is easy to prove. We add our two lemmas as hints and perform standard automation with pattern-matching of subterms to destruct. *) | 838 (** Now the final theorem is easy to prove. *) |
831 (* end thide *) | 839 (* end thide *) |
832 | 840 |
833 Theorem cfold_correct : forall t (e : exp' t), | 841 Theorem cfold_correct : forall t (e : exp' t), |
834 exp'Denote (cfold e) = exp'Denote e. | 842 exp'Denote (cfold e) = exp'Denote e. |
835 (* begin thide *) | 843 (* begin thide *) |
841 | [ |- context[cfold ?E] ] => dep_destruct (cfold E) | 849 | [ |- context[cfold ?E] ] => dep_destruct (cfold E) |
842 end; crush). | 850 end; crush). |
843 Qed. | 851 Qed. |
844 (* end thide *) | 852 (* end thide *) |
845 | 853 |
854 (** We add our two lemmas as hints and perform standard automation with pattern-matching of subterms to destruct. *) | |
846 | 855 |
847 (** * Choosing Between Representations *) | 856 (** * Choosing Between Representations *) |
848 | 857 |
849 (** It is not always clear which of these representation techniques to apply in a particular situation, but I will try to summarize the pros and cons of each. | 858 (** It is not always clear which of these representation techniques to apply in a particular situation, but I will try to summarize the pros and cons of each. |
850 | 859 |
851 Inductive types are often the most pleasant to work with, after someone has spent the time implementing some basic library functions for them, using fancy [match] annotations. Many aspects of Coq's logic and tactic support are specialized to deal with inductive types, and you may miss out if you use alternate encodings. | 860 Inductive types are often the most pleasant to work with, after someone has spent the time implementing some basic library functions for them, using fancy [match] annotations. Many aspects of Coq's logic and tactic support are specialized to deal with inductive types, and you may miss out if you use alternate encodings. |
852 | 861 |
853 Recursive types usually involve much less initial effort, but they can be less convenient to use with proof automation. For instance, the [simpl] tactic (which is among the ingredients in [crush]) will sometimes be overzealous in simplifying uses of functions over recursive types. Consider a call [get l f], where variable [l] has type [filist A (S n)]. The type of [l] would be simplified to an explicit pair type. In a proof involving many recursive types, this kind of unhelpful %``%#"#simplification#"#%''% can lead to rapid bloat in the sizes of subgoals. Even worse, it can prevent syntactic pattern-matching, like in cases where [filist] is expected but a pair type is found in the %``%#"#simplified#"#%''% version. The same problem applies to applications of recursive functions to values in recursive types: the recursive function call may %``%#"#simplify#"#%''% when the top-level structure of the type index but not the recursive value is known, because such functions are generally defined by recursion on the index, not the value. | 862 Recursive types usually involve much less initial effort, but they can be less convenient to use with proof automation. For instance, the [simpl] tactic (which is among the ingredients in [crush]) will sometimes be overzealous in simplifying uses of functions over recursive types. Consider a call [get l f], where variable [l] has type [filist A (S n)]. The type of [l] would be simplified to an explicit pair type. In a proof involving many recursive types, this kind of unhelpful "simplification" can lead to rapid bloat in the sizes of subgoals. Even worse, it can prevent syntactic pattern-matching, like in cases where [filist] is expected but a pair type is found in the "simplified" version. The same problem applies to applications of recursive functions to values in recursive types: the recursive function call may "simplify" when the top-level structure of the type index but not the recursive value is known, because such functions are generally defined by recursion on the index, not the value. |
854 | 863 |
855 Another disadvantage of recursive types is that they only apply to type families whose indices determine their %``%#"#skeletons.#"#%''% This is not true for all data structures; a good counterexample comes from the richly typed programming language syntax types we have used several times so far. The fact that a piece of syntax has type [Nat] tells us nothing about the tree structure of that syntax. | 864 Another disadvantage of recursive types is that they only apply to type families whose indices determine their "skeletons." This is not true for all data structures; a good counterexample comes from the richly typed programming language syntax types we have used several times so far. The fact that a piece of syntax has type [Nat] tells us nothing about the tree structure of that syntax. |
856 | 865 |
857 Finally, Coq type inference can be more helpful in constructing values in inductive types. Application of a particular constructor of that type tells Coq what to expect from the arguments, while, for instance, forming a generic pair does not make clear an intention to interpret the value as belonging to a particular recursive type. This downside can be mitigated to an extent by writing %``%#"#constructor#"#%''% functions for a recursive type, mirroring the definition of the corresponding inductive type. | 866 Finally, Coq type inference can be more helpful in constructing values in inductive types. Application of a particular constructor of that type tells Coq what to expect from the arguments, while, for instance, forming a generic pair does not make clear an intention to interpret the value as belonging to a particular recursive type. This downside can be mitigated to an extent by writing "constructor" functions for a recursive type, mirroring the definition of the corresponding inductive type. |
858 | 867 |
859 Reflexive encodings of data types are seen relatively rarely. As our examples demonstrated, manipulating index values manually can lead to hard-to-read code. A normal inductive type is generally easier to work with, once someone has gone through the trouble of implementing an induction principle manually with the techniques we studied in Chapter 3. For small developments, avoiding that kind of coding can justify the use of reflexive data structures. There are also some useful instances of %\index{co-inductive types}%co-inductive definitions with nested data structures (e.g., lists of values in the co-inductive type) that can only be deconstructed effectively with reflexive encoding of the nested structures. *) | 868 Reflexive encodings of data types are seen relatively rarely. As our examples demonstrated, manipulating index values manually can lead to hard-to-read code. A normal inductive type is generally easier to work with, once someone has gone through the trouble of implementing an induction principle manually with the techniques we studied in Chapter 3. For small developments, avoiding that kind of coding can justify the use of reflexive data structures. There are also some useful instances of %\index{co-inductive types}%co-inductive definitions with nested data structures (e.g., lists of values in the co-inductive type) that can only be deconstructed effectively with reflexive encoding of the nested structures. *) |