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".