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. *)