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