Mercurial > cpdt > repo
comparison src/MoreDep.v @ 100:070f4de92311
Templatize MoreDep
author | Adam Chlipala <adamc@hcoop.net> |
---|---|
date | Wed, 08 Oct 2008 10:01:26 -0400 |
parents | 696726af9530 |
children | bc12662ae895 |
comparison
equal
deleted
inserted
replaced
99:8c7d9b82a4a4 | 100:070f4de92311 |
---|---|
54 "ilist (?14 + n2)" | 54 "ilist (?14 + n2)" |
55 ]] | 55 ]] |
56 | 56 |
57 We see the return of a problem we have considered before. Without explicit annotations, Coq does not enrich our typing assumptions in the branches of a [match] expression. It is clear that the unification variable [?14] should be resolved to 0 in this context, so that we have [0 + n2] reducing to [n2], but Coq does not realize that. We cannot fix the problem using just the simple [return] clauses we applied in the last chapter. We need to combine a [return] clause with a new kind of annotation, an [in] clause. *) | 57 We see the return of a problem we have considered before. Without explicit annotations, Coq does not enrich our typing assumptions in the branches of a [match] expression. It is clear that the unification variable [?14] should be resolved to 0 in this context, so that we have [0 + n2] reducing to [n2], but Coq does not realize that. We cannot fix the problem using just the simple [return] clauses we applied in the last chapter. We need to combine a [return] clause with a new kind of annotation, an [in] clause. *) |
58 | 58 |
59 (* EX: Implement concatenation *) | |
60 | |
61 (* begin thide *) | |
59 Fixpoint app n1 (ls1 : ilist n1) n2 (ls2 : ilist n2) {struct ls1} : ilist (n1 + n2) := | 62 Fixpoint app n1 (ls1 : ilist n1) n2 (ls2 : ilist n2) {struct ls1} : ilist (n1 + n2) := |
60 match ls1 in (ilist n1) return (ilist (n1 + n2)) with | 63 match ls1 in (ilist n1) return (ilist (n1 + n2)) with |
61 | Nil => ls2 | 64 | Nil => ls2 |
62 | Cons _ x ls1' => Cons x (app ls1' ls2) | 65 | Cons _ x ls1' => Cons x (app ls1' ls2) |
63 end. | 66 end. |
67 (* end thide *) | |
64 | 68 |
65 (** This version of [app] passes the type checker. Using [return] alone allowed us to express a dependency of the [match] result type on the %\textit{%#<i>#value#</i>#%}% of the discriminee. What [in] adds to our arsenal is a way of expressing a dependency on the %\textit{%#<i>#type#</i>#%}% of the discriminee. Specifically, the [n1] in the [in] clause above is a %\textit{%#<i>#binding occurrence#</i>#%}% whose scope is the [return] clause. | 69 (** This version of [app] passes the type checker. Using [return] alone allowed us to express a dependency of the [match] result type on the %\textit{%#<i>#value#</i>#%}% of the discriminee. What [in] adds to our arsenal is a way of expressing a dependency on the %\textit{%#<i>#type#</i>#%}% of the discriminee. Specifically, the [n1] in the [in] clause above is a %\textit{%#<i>#binding occurrence#</i>#%}% whose scope is the [return] clause. |
66 | 70 |
67 We may use [in] clauses only to bind names for the arguments of an inductive type family. That is, each [in] clause must be an inductive type family name applied to a sequence of underscores and variable names of the proper length. The positions for %\textit{%#<i>#parameters#</i>#%}% to the type family must all be underscores. Parameters are those arguments declared with section variables or with entries to the left of the first colon in an inductive definition. They cannot vary depending on which constructor was used to build the discriminee, so Coq prohibits pointless matches on them. It is those arguments defined in the type to the right of the colon that we may name with [in] clauses. | 71 We may use [in] clauses only to bind names for the arguments of an inductive type family. That is, each [in] clause must be an inductive type family name applied to a sequence of underscores and variable names of the proper length. The positions for %\textit{%#<i>#parameters#</i>#%}% to the type family must all be underscores. Parameters are those arguments declared with section variables or with entries to the left of the first colon in an inductive definition. They cannot vary depending on which constructor was used to build the discriminee, so Coq prohibits pointless matches on them. It is those arguments defined in the type to the right of the colon that we may name with [in] clauses. |
68 | 72 |
69 Our [app] function could be typed in so-called %\textit{%#<i>#stratified#</i>#%}% type systems, which avoid true dependency. We could consider the length indices to lists to live in a separate, compile-time-only universe from the lists themselves. 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. *) | 73 Our [app] function could be typed in so-called %\textit{%#<i>#stratified#</i>#%}% type systems, which avoid true dependency. We could consider the length indices to lists to live in a separate, compile-time-only universe from the lists themselves. 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. *) |
70 | 74 |
75 (* EX: Implement injection from normal lists *) | |
76 | |
77 (* begin thide *) | |
71 Fixpoint inject (ls : list A) : ilist (length ls) := | 78 Fixpoint inject (ls : list A) : ilist (length ls) := |
72 match ls return (ilist (length ls)) with | 79 match ls return (ilist (length ls)) with |
73 | nil => Nil | 80 | nil => Nil |
74 | h :: t => Cons h (inject t) | 81 | h :: t => Cons h (inject t) |
75 end. | 82 end. |
83 end. | 90 end. |
84 | 91 |
85 Theorem inject_inverse : forall ls, unject (inject ls) = ls. | 92 Theorem inject_inverse : forall ls, unject (inject ls) = ls. |
86 induction ls; crush. | 93 induction ls; crush. |
87 Qed. | 94 Qed. |
95 (* end thide *) | |
96 | |
97 (* EX: Implement statically-checked "car"/"hd" *) | |
88 | 98 |
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. | 99 (** 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. |
90 | 100 |
91 [[ | 101 [[ |
92 Definition hd n (ls : ilist (S n)) : A := | 102 Definition hd n (ls : ilist (S n)) : A := |
121 | 131 |
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, which is undecidable. There %\textit{%#<i>#are#</i>#%}% 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. | 132 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, which is undecidable. There %\textit{%#<i>#are#</i>#%}% 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 | 133 |
124 Our final, working attempt at [hd] uses an auxiliary function and a surprising [return] annotation. *) | 134 Our final, working attempt at [hd] uses an auxiliary function and a surprising [return] annotation. *) |
125 | 135 |
136 (* begin thide *) | |
126 Definition hd' n (ls : ilist n) := | 137 Definition hd' n (ls : ilist n) := |
127 match ls in (ilist n) return (match n with O => unit | S _ => A end) with | 138 match ls in (ilist n) return (match n with O => unit | S _ => A end) with |
128 | Nil => tt | 139 | Nil => tt |
129 | Cons _ h _ => h | 140 | Cons _ h _ => h |
130 end. | 141 end. |
131 | 142 |
132 Definition hd n (ls : ilist (S n)) : A := hd' ls. | 143 Definition hd n (ls : ilist (S n)) : A := hd' ls. |
144 (* end thide *) | |
133 | 145 |
134 (** 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]. *) | 146 (** 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]. *) |
135 | 147 |
136 End ilist. | 148 End ilist. |
137 | 149 |
203 Error: The reference t2 was not found in the current environment | 215 Error: The reference t2 was not found in the current environment |
204 ]] | 216 ]] |
205 | 217 |
206 We run again into the problem of not being able to specify non-variable arguments in [in] clauses. The problem would just be hopeless without a use of an [in] clause, though, since the result type of the [match] depends on an argument to [exp]. Our solution will be to use a more general type, as we did for [hd]. First, we define a type-valued function to use in assigning a type to [pairOut]. *) | 218 We run again into the problem of not being able to specify non-variable arguments in [in] clauses. The problem would just be hopeless without a use of an [in] clause, though, since the result type of the [match] depends on an argument to [exp]. Our solution will be to use a more general type, as we did for [hd]. First, we define a type-valued function to use in assigning a type to [pairOut]. *) |
207 | 219 |
220 (* EX: Define a function [pairOut : forall t1 t2, exp (Prod t1 t2) -> option (exp t1 * exp t2)] *) | |
221 | |
222 (* begin thide *) | |
208 Definition pairOutType (t : type) := | 223 Definition pairOutType (t : type) := |
209 match t with | 224 match t with |
210 | Prod t1 t2 => option (exp t1 * exp t2) | 225 | Prod t1 t2 => option (exp t1 * exp t2) |
211 | _ => unit | 226 | _ => unit |
212 end. | 227 end. |
224 Definition pairOut t (e : exp t) := | 239 Definition pairOut t (e : exp t) := |
225 match e in (exp t) return (pairOutType t) with | 240 match e in (exp t) return (pairOutType t) with |
226 | Pair _ _ e1 e2 => Some (e1, e2) | 241 | Pair _ _ e1 e2 => Some (e1, e2) |
227 | _ => pairOutDefault _ | 242 | _ => pairOutDefault _ |
228 end. | 243 end. |
244 (* end thide *) | |
229 | 245 |
230 (** 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, but is similar to what goes on with Haskell type classes. | 246 (** 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, but is similar to what goes on with Haskell type classes. |
231 | 247 |
232 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. *) | 248 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. *) |
233 | 249 |
281 end. | 297 end. |
282 | 298 |
283 (** The correctness theorem for [cfold] turns out to be easy to prove, once we get over one serious hurdle. *) | 299 (** The correctness theorem for [cfold] turns out to be easy to prove, once we get over one serious hurdle. *) |
284 | 300 |
285 Theorem cfold_correct : forall t (e : exp t), expDenote e = expDenote (cfold e). | 301 Theorem cfold_correct : forall t (e : exp t), expDenote e = expDenote (cfold e). |
302 (* begin thide *) | |
286 induction e; crush. | 303 induction e; crush. |
287 | 304 |
288 (** The first remaining subgoal is: | 305 (** The first remaining subgoal is: |
289 | 306 |
290 [[ | 307 [[ |
340 repeat (match goal with | 357 repeat (match goal with |
341 | [ |- context[cfold ?E] ] => dep_destruct (cfold E) | 358 | [ |- context[cfold ?E] ] => dep_destruct (cfold E) |
342 | [ |- (if ?E then _ else _) = _ ] => destruct E | 359 | [ |- (if ?E then _ else _) = _ ] => destruct E |
343 end; crush). | 360 end; crush). |
344 Qed. | 361 Qed. |
362 (* end thide *) | |
345 | 363 |
346 | 364 |
347 (** Dependently-Typed Red-Black Trees *) | 365 (** Dependently-Typed Red-Black Trees *) |
366 | |
367 (** TODO: Add commentary for this section. *) | |
348 | 368 |
349 Inductive color : Set := Red | Black. | 369 Inductive color : Set := Red | Black. |
350 | 370 |
351 Inductive rbtree : color -> nat -> Set := | 371 Inductive rbtree : color -> nat -> Set := |
352 | Leaf : rbtree Black 0 | 372 | Leaf : rbtree Black 0 |
353 | RedNode : forall n, rbtree Black n -> nat-> rbtree Black n -> rbtree Red n | 373 | RedNode : forall n, rbtree Black n -> nat-> rbtree Black n -> rbtree Red n |
354 | BlackNode : forall c1 c2 n, rbtree c1 n -> nat -> rbtree c2 n -> rbtree Black (S n). | 374 | BlackNode : forall c1 c2 n, rbtree c1 n -> nat -> rbtree c2 n -> rbtree Black (S n). |
355 | 375 |
376 (* EX: Prove that every [rbtree] is balanced. *) | |
377 | |
378 (* begin thide *) | |
356 Require Import Max Min. | 379 Require Import Max Min. |
357 | 380 |
358 Section depth. | 381 Section depth. |
359 Variable f : nat -> nat -> nat. | 382 Variable f : nat -> nat -> nat. |
360 | 383 |
378 | Black => depth max t <= 2 * n | 401 | Black => depth max t <= 2 * n |
379 end. | 402 end. |
380 induction t; crush; | 403 induction t; crush; |
381 match goal with | 404 match goal with |
382 | [ |- context[max ?X ?Y] ] => destruct (max_dec X Y) | 405 | [ |- context[max ?X ?Y] ] => destruct (max_dec X Y) |
383 end; crush. | 406 end; crush; |
384 | 407 repeat (match goal with |
385 destruct c1; crush. | 408 | [ H : context[match ?C with Red => _ | Black => _ end] |- _ ] => destruct C |
386 destruct c2; crush. | 409 end; crush). |
387 Qed. | 410 Qed. |
388 | 411 |
389 Theorem depth_max : forall c n (t : rbtree c n), depth max t <= 2 * n + 1. | 412 Theorem depth_max : forall c n (t : rbtree c n), depth max t <= 2 * n + 1. |
390 intros; generalize (depth_max' t); destruct c; crush. | 413 intros; generalize (depth_max' t); destruct c; crush. |
391 Qed. | 414 Qed. |
392 | 415 |
393 Theorem balanced : forall c n (t : rbtree c n), 2 * depth min t + 1 >= depth max t. | 416 Theorem balanced : forall c n (t : rbtree c n), 2 * depth min t + 1 >= depth max t. |
394 intros; generalize (depth_min t); generalize (depth_max t); crush. | 417 intros; generalize (depth_min t); generalize (depth_max t); crush. |
395 Qed. | 418 Qed. |
419 (* end thide *) | |
396 | 420 |
397 | 421 |
398 Inductive rtree : nat -> Set := | 422 Inductive rtree : nat -> Set := |
399 | RedNode' : forall c1 c2 n, rbtree c1 n -> nat -> rbtree c2 n -> rtree n. | 423 | RedNode' : forall c1 c2 n, rbtree c1 n -> nat -> rbtree c2 n -> rtree n. |
400 | 424 |
411 Definition rpresent n (t : rtree n) : Prop := | 435 Definition rpresent n (t : rtree n) : Prop := |
412 match t with | 436 match t with |
413 | RedNode' _ _ _ a y b => present a \/ x = y \/ present b | 437 | RedNode' _ _ _ a y b => present a \/ x = y \/ present b |
414 end. | 438 end. |
415 End present. | 439 End present. |
440 | |
441 Locate "{ _ : _ & _ }". | |
442 Print sigT. | |
416 | 443 |
417 Notation "{< x >}" := (existT _ _ x). | 444 Notation "{< x >}" := (existT _ _ x). |
418 | 445 |
419 Definition balance1 n (a : rtree n) (data : nat) c2 := | 446 Definition balance1 n (a : rtree n) (data : nat) c2 := |
420 match a in rtree n return rbtree c2 n -> { c : color & rbtree c (S n) } with | 447 match a in rtree n return rbtree c2 n -> { c : color & rbtree c (S n) } with |