Mercurial > cpdt > repo
comparison src/MoreDep.v @ 454:49bd155dfc52
Proofreading pass through Chapter 8
author | Adam Chlipala <adam@chlipala.net> |
---|---|
date | Mon, 27 Aug 2012 17:21:34 -0400 |
parents | 2740b8a23cce |
children | 4320c1a967c2 |
comparison
equal
deleted
inserted
replaced
453:e3fa35c750ac | 454:49bd155dfc52 |
---|---|
63 Our [app] function could be typed in so-called%\index{stratified type systems}% _stratified_ type systems, which avoid true dependency. That is, we could consider the length indices to lists to live in a separate, compile-time-only universe from the lists themselves. This stratification between a compile-time universe and a run-time universe, with no references to the latter in the former, gives rise to the terminology "stratified." Our next example would be harder to implement in a stratified system. We write an injection function from regular lists to length-indexed lists. A stratified implementation would need to duplicate the definition of lists across compile-time and run-time versions, and the run-time versions would need to be indexed by the compile-time versions. *) | 63 Our [app] function could be typed in so-called%\index{stratified type systems}% _stratified_ type systems, which avoid true dependency. That is, we could consider the length indices to lists to live in a separate, compile-time-only universe from the lists themselves. This stratification between a compile-time universe and a run-time universe, with no references to the latter in the former, gives rise to the terminology "stratified." Our next example would be harder to implement in a stratified system. We write an injection function from regular lists to length-indexed lists. A stratified implementation would need to duplicate the definition of lists across compile-time and run-time versions, and the run-time versions would need to be indexed by the compile-time versions. *) |
64 | 64 |
65 (* EX: Implement injection from normal lists *) | 65 (* EX: Implement injection from normal lists *) |
66 | 66 |
67 (* begin thide *) | 67 (* begin thide *) |
68 Fixpoint inject (ls : list A) : ilist (length ls) := | 68 Fixpoint inject (ls : list A) : ilist (length ls) := |
69 match ls with | 69 match ls with |
70 | nil => Nil | 70 | nil => Nil |
71 | h :: t => Cons h (inject t) | 71 | h :: t => Cons h (inject t) |
72 end. | 72 end. |
73 | 73 |
74 (** We can define an inverse conversion and prove that it really is an inverse. *) | 74 (** We can define an inverse conversion and prove that it really is an inverse. *) |
75 | 75 |
76 Fixpoint unject n (ls : ilist n) : list A := | 76 Fixpoint unject n (ls : ilist n) : list A := |
77 match ls with | 77 match ls with |
78 | Nil => nil | 78 | Nil => nil |
79 | Cons _ h t => h :: unject t | 79 | Cons _ h t => h :: unject t |
80 end. | 80 end. |
81 | 81 |
82 Theorem inject_inverse : forall ls, unject (inject ls) = ls. | 82 Theorem inject_inverse : forall ls, unject (inject ls) = ls. |
83 induction ls; crush. | 83 induction ls; crush. |
84 Qed. | 84 Qed. |
85 (* end thide *) | 85 (* end thide *) |
86 | 86 |
87 (* EX: Implement statically checked "car"/"hd" *) | 87 (* EX: Implement statically checked "car"/"hd" *) |
88 | 88 |
89 (** Now let us attempt a function that is surprisingly tricky to write. In ML, the list head function raises an exception when passed an empty list. With length-indexed lists, we can rule out such invalid calls statically, and here is a first attempt at doing so. We write [???] as a placeholder for a term that we do not know how to write, not for any real Coq notation like those introduced two chapters ago. | 89 (** Now let us attempt a function that is surprisingly tricky to write. In ML, the list head function raises an exception when passed an empty list. With length-indexed lists, we can rule out such invalid calls statically, and here is a first attempt at doing so. We write [???] as a placeholder for a term that we do not know how to write, not for any real Coq notation like those introduced two chapters ago. |
90 [[ | 90 [[ |
91 Definition hd n (ls : ilist (S n)) : A := | 91 Definition hd n (ls : ilist (S n)) : A := |
92 match ls with | 92 match ls with |
93 | Nil => ??? | 93 | Nil => ??? |
94 | Cons _ h _ => h | 94 | Cons _ h _ => h |
95 end. | 95 end. |
96 ]] | 96 ]] |
97 It is not clear what to write for the [Nil] case, so we are stuck before we even turn our function over to the type checker. We could try omitting the [Nil] case: | 97 It is not clear what to write for the [Nil] case, so we are stuck before we even turn our function over to the type checker. We could try omitting the [Nil] case: |
98 [[ | 98 [[ |
99 Definition hd n (ls : ilist (S n)) : A := | 99 Definition hd n (ls : ilist (S n)) : A := |
100 match ls with | 100 match ls with |
101 | Cons _ h _ => h | 101 | Cons _ h _ => h |
102 end. | 102 end. |
103 ]] | 103 ]] |
104 | 104 |
105 << | 105 << |
106 Error: Non exhaustive pattern-matching: no clause found for pattern Nil | 106 Error: Non exhaustive pattern-matching: no clause found for pattern Nil |
107 >> | 107 >> |
108 | 108 |
109 Unlike in ML, we cannot use inexhaustive pattern matching, because there is no conception of a <<Match>> exception to be thrown. In fact, recent versions of Coq _do_ allow this, by implicit translation to a [match] that considers all constructors. It is educational to discover that encoding ourselves directly. We might try using an [in] clause somehow. | 109 Unlike in ML, we cannot use inexhaustive pattern matching, because there is no conception of a <<Match>> exception to be thrown. In fact, recent versions of Coq _do_ allow this, by implicit translation to a [match] that considers all constructors. It is educational to discover that encoding ourselves directly. We might try using an [in] clause somehow. |
110 | 110 |
111 [[ | 111 [[ |
112 Definition hd n (ls : ilist (S n)) : A := | 112 Definition hd n (ls : ilist (S n)) : A := |
113 match ls in (ilist (S n)) with | 113 match ls in (ilist (S n)) with |
114 | Cons _ h _ => h | 114 | Cons _ h _ => h |
115 end. | 115 end. |
116 ]] | 116 ]] |
117 | 117 |
118 << | 118 << |
119 Error: The reference n was not found in the current environment | 119 Error: The reference n was not found in the current environment |
120 >> | 120 >> |
122 In this and other cases, we feel like we want [in] clauses with type family arguments that are not variables. Unfortunately, Coq only supports variables in those positions. A completely general mechanism could only be supported with a solution to the problem of higher-order unification%~\cite{HOU}%, which is undecidable. There _are_ useful heuristics for handling non-variable indices which are gradually making their way into Coq, but we will spend some time in this and the next few chapters on effective pattern matching on dependent types using only the primitive [match] annotations. | 122 In this and other cases, we feel like we want [in] clauses with type family arguments that are not variables. Unfortunately, Coq only supports variables in those positions. A completely general mechanism could only be supported with a solution to the problem of higher-order unification%~\cite{HOU}%, which is undecidable. There _are_ useful heuristics for handling non-variable indices which are gradually making their way into Coq, but we will spend some time in this and the next few chapters on effective pattern matching on dependent types using only the primitive [match] annotations. |
123 | 123 |
124 Our final, working attempt at [hd] uses an auxiliary function and a surprising [return] annotation. *) | 124 Our final, working attempt at [hd] uses an auxiliary function and a surprising [return] annotation. *) |
125 | 125 |
126 (* begin thide *) | 126 (* begin thide *) |
127 Definition hd' n (ls : ilist n) := | 127 Definition hd' n (ls : ilist n) := |
128 match ls in (ilist n) return (match n with O => unit | S _ => A end) with | 128 match ls in (ilist n) return (match n with O => unit | S _ => A end) with |
129 | Nil => tt | 129 | Nil => tt |
130 | Cons _ h _ => h | 130 | Cons _ h _ => h |
131 end. | 131 end. |
132 | 132 |
133 Check hd'. | 133 Check hd'. |
134 (** %\vspace{-.15in}% [[ | 134 (** %\vspace{-.15in}% [[ |
135 hd' | 135 hd' |
136 : forall n : nat, ilist n -> match n with | 136 : forall n : nat, ilist n -> match n with |
137 | 0 => unit | 137 | 0 => unit |
138 | S _ => A | 138 | S _ => A |
139 end | 139 end |
140 ]] | 140 ]] |
141 *) | 141 *) |
142 | 142 |
143 Definition hd n (ls : ilist (S n)) : A := hd' ls. | 143 Definition hd n (ls : ilist (S n)) : A := hd' ls. |
144 (* end thide *) | 144 (* end thide *) |
145 | 145 |
146 End ilist. | 146 End ilist. |
147 | 147 |
148 (** We annotate our main [match] with a type that is itself a [match]. We write that the function [hd'] returns [unit] when the list is empty and returns the carried type [A] in all other cases. In the definition of [hd], we just call [hd']. Because the index of [ls] is known to be nonzero, the type checker reduces the [match] in the type of [hd'] to [A]. *) | 148 (** We annotate our main [match] with a type that is itself a [match]. We write that the function [hd'] returns [unit] when the list is empty and returns the carried type [A] in all other cases. In the definition of [hd], we just call [hd']. Because the index of [ls] is known to be nonzero, the type checker reduces the [match] in the type of [hd'] to [A]. *) |
278 end. | 278 end. |
279 (* end thide *) | 279 (* end thide *) |
280 | 280 |
281 (** There is one important subtlety in this definition. Coq allows us to use convenient ML-style pattern matching notation, but, internally and in proofs, we see that patterns are expanded out completely, matching one level of inductive structure at a time. Thus, the default case in the [match] above expands out to one case for each constructor of [exp] besides [Pair], and the underscore in [pairOutDefault _] is resolved differently in each case. From an ML or Haskell programmer's perspective, what we have here is type inference determining which code is run (returning either [None] or [tt]), which goes beyond what is possible with type inference guiding parametric polymorphism in Hindley-Milner languages%\index{Hindley-Milner}%, but is similar to what goes on with Haskell type classes%\index{type classes}%. | 281 (** There is one important subtlety in this definition. Coq allows us to use convenient ML-style pattern matching notation, but, internally and in proofs, we see that patterns are expanded out completely, matching one level of inductive structure at a time. Thus, the default case in the [match] above expands out to one case for each constructor of [exp] besides [Pair], and the underscore in [pairOutDefault _] is resolved differently in each case. From an ML or Haskell programmer's perspective, what we have here is type inference determining which code is run (returning either [None] or [tt]), which goes beyond what is possible with type inference guiding parametric polymorphism in Hindley-Milner languages%\index{Hindley-Milner}%, but is similar to what goes on with Haskell type classes%\index{type classes}%. |
282 | 282 |
283 With [pairOut] available, we can write [cfold] in a straightforward way. There are really no surprises beyond that Coq verifies that this code has such an expressive type, given the small annotation burden. In some places, we see that Coq's [match] annotation inference is too smart for its own good, and we have to turn that inference with explicit [return] clauses. *) | 283 With [pairOut] available, we can write [cfold] in a straightforward way. There are really no surprises beyond that Coq verifies that this code has such an expressive type, given the small annotation burden. In some places, we see that Coq's [match] annotation inference is too smart for its own good, and we have to turn that inference off with explicit [return] clauses. *) |
284 | 284 |
285 Fixpoint cfold t (e : exp t) : exp t := | 285 Fixpoint cfold t (e : exp t) : exp t := |
286 match e with | 286 match e with |
287 | NConst n => NConst n | 287 | NConst n => NConst n |
288 | Plus e1 e2 => | 288 | Plus e1 e2 => |
365 | Snd _ _ _ => Plus (cfold e1) (cfold e2) | 365 | Snd _ _ _ => Plus (cfold e1) (cfold e2) |
366 end | 366 end |
367 | 367 |
368 ]] | 368 ]] |
369 | 369 |
370 We would like to do a case analysis on [cfold e1], and we attempt that in the way that has worked so far. | 370 We would like to do a case analysis on [cfold e1], and we attempt to do so in the way that has worked so far. |
371 [[ | 371 [[ |
372 destruct (cfold e1). | 372 destruct (cfold e1). |
373 ]] | 373 ]] |
374 | 374 |
375 << | 375 << |
757 present_insert. | 757 present_insert. |
758 Qed. | 758 Qed. |
759 End present. | 759 End present. |
760 End insert. | 760 End insert. |
761 | 761 |
762 (** We can generate executable OCaml code with the command %\index{Vernacular commands!Recursive Extraction}%[Recursive Extraction insert], which also automatically outputs the OCaml versions of all of [insert]'s dependencies. In our previous extractions, we wound up with clean OCaml code. Here, we find uses of %\index{Obj.magic}%<<Obj.magic>>, OCaml's unsafe cast operator for tweaking the apparent type of an expression in an arbitrary way. Casts appear for this example because the return type of [insert] depends on the _value_ of the function's argument, a pattern which OCaml cannot handle. Since Coq's type system is much more expressive than OCaml's, such casts are unavoidable in general. Since the OCaml type-checker is no longer checking full safety of programs, we must rely on Coq's extractor to use casts only in provably safe ways. *) | 762 (** We can generate executable OCaml code with the command %\index{Vernacular commands!Recursive Extraction}%[Recursive Extraction insert], which also automatically outputs the OCaml versions of all of [insert]'s dependencies. In our previous extractions, we wound up with clean OCaml code. Here, we find uses of %\index{Obj.magic}%<<Obj.magic>>, OCaml's unsafe cast operator for tweaking the apparent type of an expression in an arbitrary way. Casts appear for this example because the return type of [insert] depends on the _value_ of the function's argument, a pattern that OCaml cannot handle. Since Coq's type system is much more expressive than OCaml's, such casts are unavoidable in general. Since the OCaml type-checker is no longer checking full safety of programs, we must rely on Coq's extractor to use casts only in provably safe ways. *) |
763 | 763 |
764 (* begin hide *) | 764 (* begin hide *) |
765 Recursive Extraction insert. | 765 Recursive Extraction insert. |
766 (* end hide *) | 766 (* end hide *) |
767 | 767 |
797 | 797 |
798 << | 798 << |
799 User error: Large non-propositional inductive types must be in Type | 799 User error: Large non-propositional inductive types must be in Type |
800 >> | 800 >> |
801 | 801 |
802 What is a %\index{large inductive types}%large inductive type? In Coq, it is an inductive type that has a constructor which quantifies over some type of type [Type]. We have not worked with [Type] very much to this point. Every term of CIC has a type, including [Set] and [Prop], which are assigned type [Type]. The type [string -> Prop] from the failed definition also has type [Type]. | 802 What is a %\index{large inductive types}%large inductive type? In Coq, it is an inductive type that has a constructor that quantifies over some type of type [Type]. We have not worked with [Type] very much to this point. Every term of CIC has a type, including [Set] and [Prop], which are assigned type [Type]. The type [string -> Prop] from the failed definition also has type [Type]. |
803 | 803 |
804 It turns out that allowing large inductive types in [Set] leads to contradictions when combined with certain kinds of classical logic reasoning. Thus, by default, such types are ruled out. There is a simple fix for our [regexp] definition, which is to place our new type in [Type]. While fixing the problem, we also expand the list of constructors to cover the remaining regular expression operators. *) | 804 It turns out that allowing large inductive types in [Set] leads to contradictions when combined with certain kinds of classical logic reasoning. Thus, by default, such types are ruled out. There is a simple fix for our [regexp] definition, which is to place our new type in [Type]. While fixing the problem, we also expand the list of constructors to cover the remaining regular expression operators. *) |
805 | 805 |
806 Inductive regexp : (string -> Prop) -> Type := | 806 Inductive regexp : (string -> Prop) -> Type := |
807 | Char : forall ch : ascii, | 807 | Char : forall ch : ascii, |
913 [[ | 913 [[ |
914 | S n' => fun _ => let n := S n' in | 914 | S n' => fun _ => let n := S n' in |
915 (P1_dec (substring 0 n s) | 915 (P1_dec (substring 0 n s) |
916 && P2_dec (substring n (length s - n) s)) | 916 && P2_dec (substring n (length s - n) s)) |
917 || F n' _ | 917 || F n' _ |
918 | |
919 ]] | 918 ]] |
920 | 919 |
921 The [split] function itself is trivial to implement in terms of [split']. We just ask [split'] to begin its search with [n = length s]. *) | 920 The [split] function itself is trivial to implement in terms of [split']. We just ask [split'] to begin its search with [n = length s]. *) |
922 | 921 |
923 Definition split : {exists s1, exists s2, s = s1 ++ s2 /\ P1 s1 /\ P2 s2} | 922 Definition split : {exists s1, exists s2, s = s1 ++ s2 /\ P1 s1 /\ P2 s2} |
1096 | 1095 |
1097 (** The function [dec_star''] implements a single iteration of the star. That is, it tries to find a string prefix matching [P], and it calls a parameter function on the remainder of the string. *) | 1096 (** The function [dec_star''] implements a single iteration of the star. That is, it tries to find a string prefix matching [P], and it calls a parameter function on the remainder of the string. *) |
1098 | 1097 |
1099 Section dec_star''. | 1098 Section dec_star''. |
1100 Variable n : nat. | 1099 Variable n : nat. |
1101 (** [n] is the length of the prefix of [s] that we have already processed. *) | 1100 (** Variable [n] is the length of the prefix of [s] that we have already processed. *) |
1102 | 1101 |
1103 Variable P' : string -> Prop. | 1102 Variable P' : string -> Prop. |
1104 Variable P'_dec : forall n' : nat, n' > n | 1103 Variable P'_dec : forall n' : nat, n' > n |
1105 -> {P' (substring n' (length s - n') s)} | 1104 -> {P' (substring n' (length s - n') s)} |
1106 + {~ P' (substring n' (length s - n') s)}. | 1105 + {~ P' (substring n' (length s - n') s)}. |
1222 Eval hnf in matches a_b "a". | 1221 Eval hnf in matches a_b "a". |
1223 Eval hnf in matches a_b "aa". | 1222 Eval hnf in matches a_b "aa". |
1224 Eval hnf in matches a_b "b". | 1223 Eval hnf in matches a_b "b". |
1225 (* end hide *) | 1224 (* end hide *) |
1226 | 1225 |
1227 (** Many regular expression matching problems are easy to test. The reader may run each of the following queries to verify that it gives the correct answer. We use evaluation strategy %\index{tactics!hnf}%[hnf] to reduce each term to%\index{head-normal form}% _head-normal form_, where the datatype constructor used to build its value is known. *) | 1226 (** Many regular expression matching problems are easy to test. The reader may run each of the following queries to verify that it gives the correct answer. We use evaluation strategy %\index{tactics!hnf}%[hnf] to reduce each term to%\index{head-normal form}% _head-normal form_, where the datatype constructor used to build its value is known. (Further reduction would involve wasteful simplification of proof terms justifying the answers of our procedures.) *) |
1228 | 1227 |
1229 Example a_star := Star (Char "a"%char). | 1228 Example a_star := Star (Char "a"%char). |
1230 Eval hnf in matches a_star "". | 1229 Eval hnf in matches a_star "". |
1231 Eval hnf in matches a_star "a". | 1230 Eval hnf in matches a_star "a". |
1232 Eval hnf in matches a_star "b". | 1231 Eval hnf in matches a_star "b". |