comparison src/Generic.v @ 219:dbac52f5bce1

Port Generic
author Adam Chlipala <adamc@hcoop.net>
date Fri, 13 Nov 2009 12:03:08 -0500
parents d1464997078d
children 540a09187193
comparison
equal deleted inserted replaced
218:c8508d277a00 219:dbac52f5bce1
14 14
15 Set Implicit Arguments. 15 Set Implicit Arguments.
16 (* end hide *) 16 (* end hide *)
17 17
18 18
19 (** %\part{Chapters to be Moved Earlier} 19 (** %\chapter{Generic Programming}% *)
20 20
21 \chapter{Generic Programming}% *) 21 (** %\textit{%#<i>#Generic programming#</i>#%}% makes it possible to write functions that operate over different types of data. Parametric polymorphism in ML and Haskell is one of the simplest examples. ML-style module systems and Haskell type classes are more flexible cases. These language features are often not as powerful so we would like. For instance, while Haskell includes a type class classifying those types whose values can be pretty-printed, per-type pretty-printing is usually either implemented manually or implemented via a [deriving] clause, which triggers ad-hoc code generation. Some clever encoding tricks have been used to achieve better within Haskell and other languages, but we can do datatype-generic programming much more cleanly with dependent types. Thanks to the expressive power of CIC, we need no special language support.
22 22
23 (** TODO: Prose for this chapter *) 23 Generic programming can often be very useful in Coq developments, so we devote this chapter to studying it. In a proof assistant, there is the new possibility of generic proofs about generic programs, which we also devote some space to. *)
24
25 24
26 (** * Reflecting Datatype Definitions *) 25 (** * Reflecting Datatype Definitions *)
26
27 (** The key to generic programming with dependent types is %\textit{%#<i>#universe types#</i>#%}%. This concept should not be confused with the idea of %\textit{%#<i>#universes#</i>#%}% from the metatheory of CIC and related languages. Rather, the idea of universe types is to define inductive types that provide %\textit{%#<i>#syntactic representations#</i>#%}% of Coq types. We cannot directly write CIC programs that do case analysis on types, but we %\textit{%#<i>#can#</i>#%}% case analyze on reflected syntactic versions of those types.
28
29 Thus, to begin, we must define a syntactic representation of some class of datatypes. In this chapter, our running example will have to do with basic algebraic datatypes, of the kind found in ML and Haskell, but without additional bells and whistles like type parameters and mutually-recursive definitions.
30
31 The first step is to define a representation for constructors of our datatypes. *)
27 32
28 (* EX: Define a reflected representation of simple algebraic datatypes. *) 33 (* EX: Define a reflected representation of simple algebraic datatypes. *)
29 34
30 (* begin thide *) 35 (* begin thide *)
31 Record constructor : Type := Con { 36 Record constructor : Type := Con {
32 nonrecursive : Type; 37 nonrecursive : Type;
33 recursive : nat 38 recursive : nat
34 }. 39 }.
35 40
41 (** The idea is that a constructor represented as [Con T n] has [n] arguments of the type that we are defining. Additionally, all of the other, non-recursive arguments can be encoded in the type [T]. When there are no non-recursive arguments, [T] can be [unit]. When there are two non-recursive arguments, of types [A] and [B], [T] can be [A * B]. We can generalizer to any number of arguments via tupling.
42
43 With this definition, it as easy to define a datatype representation in terms of lists of constructors. *)
44
36 Definition datatype := list constructor. 45 Definition datatype := list constructor.
46
47 (** Here are a few example encodings for some common types from the Coq standard library. While our syntax type does not support type parameters directly, we can implement them at the meta level, via functions from types to [datatype]s. *)
37 48
38 Definition Empty_set_dt : datatype := nil. 49 Definition Empty_set_dt : datatype := nil.
39 Definition unit_dt : datatype := Con unit 0 :: nil. 50 Definition unit_dt : datatype := Con unit 0 :: nil.
40 Definition bool_dt : datatype := Con unit 0 :: Con unit 0 :: nil. 51 Definition bool_dt : datatype := Con unit 0 :: Con unit 0 :: nil.
41 Definition nat_dt : datatype := Con unit 0 :: Con unit 1 :: nil. 52 Definition nat_dt : datatype := Con unit 0 :: Con unit 1 :: nil.
42 Definition list_dt (A : Type) : datatype := Con unit 0 :: Con A 1 :: nil. 53 Definition list_dt (A : Type) : datatype := Con unit 0 :: Con A 1 :: nil.
54
55 (** [Empty_set] has no constructors, so its representation is the empty list. [unit] has one constructor with no arguments, so its one reflected constructor indicates no non-recursive data and [0] recursive arguments. The representation for [bool] just duplicates this single argumentless constructor. We get from [bool] to [nat] by changing one of the constructors to indicate 1 recursive argument. We get from [nat] to [list] by adding a non-recursive argument of a parameter type [A].
56
57 As a further example, we can do the same encoding for a generic binary tree type. *)
58
43 (* end thide *) 59 (* end thide *)
44 60
45 Section tree. 61 Section tree.
46 Variable A : Type. 62 Variable A : Type.
47 63
51 End tree. 67 End tree.
52 68
53 (* begin thide *) 69 (* begin thide *)
54 Definition tree_dt (A : Type) : datatype := Con A 0 :: Con unit 2 :: nil. 70 Definition tree_dt (A : Type) : datatype := Con A 0 :: Con unit 2 :: nil.
55 71
72 (** Each datatype representation stands for a family of inductive types. For a specific real datatype and a reputed representation for it, it is useful to define a type of %\textit{%#<i>#evidence#</i>#%}% that the datatype is compatible with the encoding. *)
73
56 Section denote. 74 Section denote.
57 Variable T : Type. 75 Variable T : Type.
76 (** This variable stands for the concrete datatype that we are interested in. *)
58 77
59 Definition constructorDenote (c : constructor) := 78 Definition constructorDenote (c : constructor) :=
60 nonrecursive c -> ilist T (recursive c) -> T. 79 nonrecursive c -> ilist T (recursive c) -> T.
80 (** We write that a constructor is represented as a function returning a [T]. Such a function takes two arguments, which pack together the non-recursive and recursive arguments of the constructor. We represent a tuple of all recursive arguments using the length-indexed list type [ilist] that we met in Chapter 7. *)
61 81
62 Definition datatypeDenote := hlist constructorDenote. 82 Definition datatypeDenote := hlist constructorDenote.
83 (** Finally, the evidence for type [T] is a hetergeneous list, including a constructor denotation for every constructor encoding in a datatype encoding. Recall that, since we are inside a section binding [T] as a variable, [constructorDenote] is automatically parameterized by [T]. *)
84
63 End denote. 85 End denote.
64 (* end thide *) 86 (* end thide *)
87
88 (** Some example pieces of evidence should help clarify the convention. First, we define some helpful notations, providing different ways of writing constructor denotations. There is really just one notation, but we need several versions of it to cover different choices of which variables will be used in the body of a definition. %The ASCII \texttt{\textasciitilde{}>} from the notation will be rendered later as $\leadsto$.% *)
89
90 (** printing ~> $\leadsto$ *)
65 91
66 Notation "[ ! , ! ~> x ]" := ((fun _ _ => x) : constructorDenote _ (Con _ _)). 92 Notation "[ ! , ! ~> x ]" := ((fun _ _ => x) : constructorDenote _ (Con _ _)).
67 Notation "[ v , ! ~> x ]" := ((fun v _ => x) : constructorDenote _ (Con _ _)). 93 Notation "[ v , ! ~> x ]" := ((fun v _ => x) : constructorDenote _ (Con _ _)).
68 Notation "[ ! , r # n ~> x ]" := ((fun _ r => x) : constructorDenote _ (Con _ n)). 94 Notation "[ ! , r ~> x ]" := ((fun _ r => x) : constructorDenote _ (Con _ _)).
69 Notation "[ v , r # n ~> x ]" := ((fun v r => x) : constructorDenote _ (Con _ n)). 95 Notation "[ v , r ~> x ]" := ((fun v r => x) : constructorDenote _ (Con _ _)).
70 96
71 (* begin thide *) 97 (* begin thide *)
72 Definition Empty_set_den : datatypeDenote Empty_set Empty_set_dt := 98 Definition Empty_set_den : datatypeDenote Empty_set Empty_set_dt :=
73 HNil. 99 HNil.
74 Definition unit_den : datatypeDenote unit unit_dt := 100 Definition unit_den : datatypeDenote unit unit_dt :=
75 [!, ! ~> tt] ::: HNil. 101 [!, ! ~> tt] ::: HNil.
76 Definition bool_den : datatypeDenote bool bool_dt := 102 Definition bool_den : datatypeDenote bool bool_dt :=
77 [!, ! ~> true] ::: [!, ! ~> false] ::: HNil. 103 [!, ! ~> true] ::: [!, ! ~> false] ::: HNil.
78 Definition nat_den : datatypeDenote nat nat_dt := 104 Definition nat_den : datatypeDenote nat nat_dt :=
79 [!, ! ~> O] ::: [!, r # 1 ~> S (hd r)] ::: HNil. 105 [!, ! ~> O] ::: [!, r ~> S (hd r)] ::: HNil.
80 Definition list_den (A : Type) : datatypeDenote (list A) (list_dt A) := 106 Definition list_den (A : Type) : datatypeDenote (list A) (list_dt A) :=
81 [!, ! ~> nil] ::: [x, r # 1 ~> x :: hd r] ::: HNil. 107 [!, ! ~> nil] ::: [x, r ~> x :: hd r] ::: HNil.
82 Definition tree_den (A : Type) : datatypeDenote (tree A) (tree_dt A) := 108 Definition tree_den (A : Type) : datatypeDenote (tree A) (tree_dt A) :=
83 [v, ! ~> Leaf v] ::: [!, r # 2 ~> Node (hd r) (hd (tl r))] ::: HNil. 109 [v, ! ~> Leaf v] ::: [!, r ~> Node (hd r) (hd (tl r))] ::: HNil.
84 (* end thide *) 110 (* end thide *)
85 111
86 112
87 (** * Recursive Definitions *) 113 (** * Recursive Definitions *)
88 114
89 (* EX: Define a generic [size] function. *) 115 (* EX: Define a generic [size] function. *)
116
117 (** We built these encodings of datatypes to help us write datatype-generic recursive functions. To do so, we will want a reflected representation of a %\textit{%#<i>#recursion scheme#</i>#%}% for each type, similar to the [T_rect] principle generated automatically for an inductive definition of [T]. A clever reuse of [datatypeDenote] yields a short definition. *)
90 118
91 (* begin thide *) 119 (* begin thide *)
92 Definition fixDenote (T : Type) (dt : datatype) := 120 Definition fixDenote (T : Type) (dt : datatype) :=
93 forall (R : Type), datatypeDenote R dt -> (T -> R). 121 forall (R : Type), datatypeDenote R dt -> (T -> R).
94 122
123 (** The idea of a recursion scheme is parameterized by a type and a reputed encoding of it. The principle itself is polymorphic in a type [R], which is the return type of the recursive function that we mean to write. The next argument is a hetergeneous list of one case of the recursive function definition for each datatype constructor. The [datatypeDenote] function turns out to have just the right definition to express the type we need; a set of function cases is just like an alternate set of constructors where we replace the original type [T] with the function result type [R]. Given such a reflected definition, a [fixDenote] invocation returns a function from [T] to [R], which is just what we wanted.
124
125 We are ready to write some example functions now. It will be useful to use one new function from the [DepList] library included in the book source. *)
126
127 Check hmake.
128 (** %\vspace{-.15in}% [[
129 hmake
130 : forall (A : Type) (B : A -> Type),
131 (forall x : A, B x) -> forall ls : list A, hlist B l
132
133 ]]
134
135 [hmake] is a kind of [map] alternative that goes from a regular [list] to an [hlist]. We can use it to define a generic size function which counts the number of constructors used to build a value in a datatype. *)
136
95 Definition size T dt (fx : fixDenote T dt) : T -> nat := 137 Definition size T dt (fx : fixDenote T dt) : T -> nat :=
96 fx nat (hmake (B := constructorDenote nat) (fun _ _ r => foldr plus 1 r) dt). 138 fx nat (hmake (B := constructorDenote nat) (fun _ _ r => foldr plus 1 r) dt).
139
140 (** Our definition is parameterized over a recursion scheme [fx]. We instantiate [fx] by passing it the function result type and a set of function cases, where we build the latter with [hmake]. The function argument to [hmake] takes three arguments: the representation of a constructor, its non-recursive arguments, and the results of recursive calls on all of its recursive arguments. We only need the recursive call results here, so we call them [r] and bind the other two inputs with wildcards. The actual case body is simple: we add together the recursive call results and increment the result by one (to account for the current constructor). This [foldr] function is an [hlist]-specific version defined in the [DepList] module.
141
142 It is instructive to build [fixDenote] values for our example types and see what specialized [size] functions result from them. *)
97 143
98 Definition Empty_set_fix : fixDenote Empty_set Empty_set_dt := 144 Definition Empty_set_fix : fixDenote Empty_set Empty_set_dt :=
99 fun R _ emp => match emp with end. 145 fun R _ emp => match emp with end.
100 Eval compute in size Empty_set_fix. 146 Eval compute in size Empty_set_fix.
147 (** %\vspace{-.15in}% [[
148 = fun emp : Empty_set => match emp return nat with
149 end
150 : Empty_set -> nat
151
152 ]]
153
154 Despite all the fanciness of the generic [size] function, CIC's standard computation rules suffice to normalize the generic function specialization to exactly what we would have written manually. *)
101 155
102 Definition unit_fix : fixDenote unit unit_dt := 156 Definition unit_fix : fixDenote unit unit_dt :=
103 fun R cases _ => (hhd cases) tt INil. 157 fun R cases _ => (hhd cases) tt INil.
104 Eval compute in size unit_fix. 158 Eval compute in size unit_fix.
159 (** %\vspace{-.15in}% [[
160 = fun _ : unit => 1
161 : unit -> nat
162
163 ]]
164
165 Again normalization gives us the natural function definition. We see this pattern repeated for our other example types. *)
105 166
106 Definition bool_fix : fixDenote bool bool_dt := 167 Definition bool_fix : fixDenote bool bool_dt :=
107 fun R cases b => if b 168 fun R cases b => if b
108 then (hhd cases) tt INil 169 then (hhd cases) tt INil
109 else (hhd (htl cases)) tt INil. 170 else (hhd (htl cases)) tt INil.
110 Eval compute in size bool_fix. 171 Eval compute in size bool_fix.
172 (** %\vspace{-.15in}% [[
173 = fun b : bool => if b then 1 else 1
174 : bool -> nat
175 ]] *)
111 176
112 Definition nat_fix : fixDenote nat nat_dt := 177 Definition nat_fix : fixDenote nat nat_dt :=
113 fun R cases => fix F (n : nat) : R := 178 fun R cases => fix F (n : nat) : R :=
114 match n with 179 match n with
115 | O => (hhd cases) tt INil 180 | O => (hhd cases) tt INil
116 | S n' => (hhd (htl cases)) tt (ICons (F n') INil) 181 | S n' => (hhd (htl cases)) tt (ICons (F n') INil)
117 end. 182 end.
183
184 (** To peek at the [size] function for [nat], it is useful to avoid full computation, so that the recursive definition of addition is not expanded inline. We can accomplish this with proper flags for the [cbv] reduction strategy. *)
185
118 Eval cbv beta iota delta -[plus] in size nat_fix. 186 Eval cbv beta iota delta -[plus] in size nat_fix.
187 (** %\vspace{-.15in}% [[
188 = fix F (n : nat) : nat := match n with
189 | 0 => 1
190 | S n' => F n' + 1
191 end
192 : nat -> nat
193 ]] *)
119 194
120 Definition list_fix (A : Type) : fixDenote (list A) (list_dt A) := 195 Definition list_fix (A : Type) : fixDenote (list A) (list_dt A) :=
121 fun R cases => fix F (ls : list A) : R := 196 fun R cases => fix F (ls : list A) : R :=
122 match ls with 197 match ls with
123 | nil => (hhd cases) tt INil 198 | nil => (hhd cases) tt INil
124 | x :: ls' => (hhd (htl cases)) x (ICons (F ls') INil) 199 | x :: ls' => (hhd (htl cases)) x (ICons (F ls') INil)
125 end. 200 end.
126 Eval cbv beta iota delta -[plus] in fun A => size (@list_fix A). 201 Eval cbv beta iota delta -[plus] in fun A => size (@list_fix A).
202 (** %\vspace{-.15in}% [[
203 = fun A : Type =>
204 fix F (ls : list A) : nat :=
205 match ls with
206 | nil => 1
207 | _ :: ls' => F ls' + 1
208 end
209 : forall A : Type, list A -> nat
210 ]] *)
127 211
128 Definition tree_fix (A : Type) : fixDenote (tree A) (tree_dt A) := 212 Definition tree_fix (A : Type) : fixDenote (tree A) (tree_dt A) :=
129 fun R cases => fix F (t : tree A) : R := 213 fun R cases => fix F (t : tree A) : R :=
130 match t with 214 match t with
131 | Leaf x => (hhd cases) x INil 215 | Leaf x => (hhd cases) x INil
132 | Node t1 t2 => (hhd (htl cases)) tt (ICons (F t1) (ICons (F t2) INil)) 216 | Node t1 t2 => (hhd (htl cases)) tt (ICons (F t1) (ICons (F t2) INil))
133 end. 217 end.
134 Eval cbv beta iota delta -[plus] in fun A => size (@tree_fix A). 218 Eval cbv beta iota delta -[plus] in fun A => size (@tree_fix A).
219 (** %\vspace{-.15in}% [[
220 = fun A : Type =>
221 fix F (t : tree A) : nat :=
222 match t with
223 | Leaf _ => 1
224 | Node t1 t2 => F t1 + (F t2 + 1)
225 end
226 : forall A : Type, tree A -> n
227 ]] *)
135 (* end thide *) 228 (* end thide *)
136 229
137 230
138 (** ** Pretty-Printing *) 231 (** ** Pretty-Printing *)
139 232
140 (* EX: Define a generic pretty-printing function. *) 233 (* EX: Define a generic pretty-printing function. *)
234
235 (** It is also useful to do generic pretty-printing of datatype values, rendering them as human-readable strings. To do so, we will need a bit of metadata for each constructor. Specifically, we need the name to print for the constructor and the function to use to render its non-recursive arguments. Everything else can be done generically. *)
141 236
142 (* begin thide *) 237 (* begin thide *)
143 Record print_constructor (c : constructor) : Type := PI { 238 Record print_constructor (c : constructor) : Type := PI {
144 printName : string; 239 printName : string;
145 printNonrec : nonrecursive c -> string 240 printNonrec : nonrecursive c -> string
146 }. 241 }.
147 242
243 (** It is useful to define a shorthand for applying the constructor [PI]. By applying it explicitly to an unknown application of the constructor [Con], we help type inference work. *)
244
148 Notation "^" := (PI (Con _ _)). 245 Notation "^" := (PI (Con _ _)).
149 246
247 (** As in earlier examples, we define the type of metadata for a datatype to be a heterogeneous list type collecting metadata for each constructor. *)
248
150 Definition print_datatype := hlist print_constructor. 249 Definition print_datatype := hlist print_constructor.
151 250
152 Open Local Scope string_scope. 251 (** We will be doing some string manipulation here, so we import the notations associated with strings. *)
252
253 Local Open Scope string_scope.
254
255 (** Now it is easy to implement our generic printer, using another function from [DepList.] *)
256
257 Check hmap.
258 (** %\vspace{-.15in}% [[
259 hmap
260 : forall (A : Type) (B1 B2 : A -> Type),
261 (forall x : A, B1 x -> B2 x) ->
262 forall ls : list A, hlist B1 ls -> hlist B2 ls
263 ]] *)
153 264
154 Definition print T dt (pr : print_datatype dt) (fx : fixDenote T dt) : T -> string := 265 Definition print T dt (pr : print_datatype dt) (fx : fixDenote T dt) : T -> string :=
155 fx string (hmap (B1 := print_constructor) (B2 := constructorDenote string) 266 fx string (hmap (B1 := print_constructor) (B2 := constructorDenote string)
156 (fun _ pc x r => printName pc ++ "(" ++ printNonrec pc x 267 (fun _ pc x r => printName pc ++ "(" ++ printNonrec pc x
157 ++ foldr (fun s acc => ", " ++ s ++ acc) ")" r) pr). 268 ++ foldr (fun s acc => ", " ++ s ++ acc) ")" r) pr).
158 (* end thide *) 269 (* end thide *)
159 270
271 (** Some simple tests establish that [print] gets the job done. *)
272
160 Eval compute in print HNil Empty_set_fix. 273 Eval compute in print HNil Empty_set_fix.
274 (** %\vspace{-.15in}% [[
275 = fun emp : Empty_set => match emp return string with
276 end
277 : Empty_set -> string
278 ]] *)
279
161 Eval compute in print (^ "tt" (fun _ => "") ::: HNil) unit_fix. 280 Eval compute in print (^ "tt" (fun _ => "") ::: HNil) unit_fix.
281 (** %\vspace{-.15in}% [[
282 = fun _ : unit => "tt()"
283 : unit -> string
284 ]] *)
285
162 Eval compute in print (^ "true" (fun _ => "") 286 Eval compute in print (^ "true" (fun _ => "")
163 ::: ^ "false" (fun _ => "") 287 ::: ^ "false" (fun _ => "")
164 ::: HNil) bool_fix. 288 ::: HNil) bool_fix.
289 (** %\vspace{-.15in}% [[
290 = fun b : bool => if b then "true()" else "false()"
291 : bool -> s
292 ]] *)
165 293
166 Definition print_nat := print (^ "O" (fun _ => "") 294 Definition print_nat := print (^ "O" (fun _ => "")
167 ::: ^ "S" (fun _ => "") 295 ::: ^ "S" (fun _ => "")
168 ::: HNil) nat_fix. 296 ::: HNil) nat_fix.
169 Eval cbv beta iota delta -[append] in print_nat. 297 Eval cbv beta iota delta -[append] in print_nat.
298 (** %\vspace{-.15in}% [[
299 = fix F (n : nat) : string :=
300 match n with
301 | 0%nat => "O" ++ "(" ++ "" ++ ")"
302 | S n' => "S" ++ "(" ++ "" ++ ", " ++ F n' ++ ")"
303 end
304 : nat -> string
305 ]] *)
306
170 Eval simpl in print_nat 0. 307 Eval simpl in print_nat 0.
308 (** %\vspace{-.15in}% [[
309 = "O()"
310 : string
311 ]] *)
312
171 Eval simpl in print_nat 1. 313 Eval simpl in print_nat 1.
314 (** %\vspace{-.15in}% [[
315 = "S(, O())"
316 : string
317 ]] *)
318
172 Eval simpl in print_nat 2. 319 Eval simpl in print_nat 2.
320 (** %\vspace{-.15in}% [[
321 = "S(, S(, O()))"
322 : string
323 ]] *)
173 324
174 Eval cbv beta iota delta -[append] in fun A (pr : A -> string) => 325 Eval cbv beta iota delta -[append] in fun A (pr : A -> string) =>
175 print (^ "nil" (fun _ => "") 326 print (^ "nil" (fun _ => "")
176 ::: ^ "cons" pr 327 ::: ^ "cons" pr
177 ::: HNil) (@list_fix A). 328 ::: HNil) (@list_fix A).
329 (** %\vspace{-.15in}% [[
330 = fun (A : Type) (pr : A -> string) =>
331 fix F (ls : list A) : string :=
332 match ls with
333 | nil => "nil" ++ "(" ++ "" ++ ")"
334 | x :: ls' => "cons" ++ "(" ++ pr x ++ ", " ++ F ls' ++ ")"
335 end
336 : forall A : Type, (A -> string) -> list A -> string
337 ]] *)
338
178 Eval cbv beta iota delta -[append] in fun A (pr : A -> string) => 339 Eval cbv beta iota delta -[append] in fun A (pr : A -> string) =>
179 print (^ "Leaf" pr 340 print (^ "Leaf" pr
180 ::: ^ "Node" (fun _ => "") 341 ::: ^ "Node" (fun _ => "")
181 ::: HNil) (@tree_fix A). 342 ::: HNil) (@tree_fix A).
343 (** %\vspace{-.15in}% [[
344 = fun (A : Type) (pr : A -> string) =>
345 fix F (t : tree A) : string :=
346 match t with
347 | Leaf x => "Leaf" ++ "(" ++ pr x ++ ")"
348 | Node t1 t2 =>
349 "Node" ++ "(" ++ "" ++ ", " ++ F t1 ++ ", " ++ F t2 ++ ")"
350 end
351 : forall A : Type, (A -> string) -> tree A -> string
352 ]] *)
182 353
183 354
184 (** ** Mapping *) 355 (** ** Mapping *)
185 356
186 (* EX: Define a generic [map] function. *) 357 (* EX: Define a generic [map] function. *)
187 358
188 (* begin thide *) 359 (** By this point, we have developed enough machinery that it is old hat to define a generic function similar to the list [map] function. *)
189 Definition map T dt (dd : datatypeDenote T dt) (fx : fixDenote T dt) (f : T -> T) : T -> T := 360
361 (* begin thide *)
362 Definition map T dt (dd : datatypeDenote T dt) (fx : fixDenote T dt) (f : T -> T)
363 : T -> T :=
190 fx T (hmap (B1 := constructorDenote T) (B2 := constructorDenote T) 364 fx T (hmap (B1 := constructorDenote T) (B2 := constructorDenote T)
191 (fun _ c x r => f (c x r)) dd). 365 (fun _ c x r => f (c x r)) dd).
192 (* end thide *) 366 (* end thide *)
193 367
194 Eval compute in map Empty_set_den Empty_set_fix. 368 Eval compute in map Empty_set_den Empty_set_fix.
369 (** %\vspace{-.15in}% [[
370 = fun (_ : Empty_set -> Empty_set) (emp : Empty_set) =>
371 match emp return Empty_set with
372 end
373 : (Empty_set -> Empty_set) -> Empty_set -> Empty_set
374 ]] *)
375
195 Eval compute in map unit_den unit_fix. 376 Eval compute in map unit_den unit_fix.
377 (** %\vspace{-.15in}% [[
378 = fun (f : unit -> unit) (_ : unit) => f tt
379 : (unit -> unit) -> unit -> unit
380 ]] *)
381
196 Eval compute in map bool_den bool_fix. 382 Eval compute in map bool_den bool_fix.
383 (** %\vspace{-.15in}% [[
384 = fun (f : bool -> bool) (b : bool) => if b then f true else f false
385 : (bool -> bool) -> bool -> bool
386 ]] *)
387
197 Eval compute in map nat_den nat_fix. 388 Eval compute in map nat_den nat_fix.
389 (** %\vspace{-.15in}% [[
390 = fun f : nat -> nat =>
391 fix F (n : nat) : nat :=
392 match n with
393 | 0%nat => f 0%nat
394 | S n' => f (S (F n'))
395 end
396 : (nat -> nat) -> nat -> nat
397 ]] *)
398
198 Eval compute in fun A => map (list_den A) (@list_fix A). 399 Eval compute in fun A => map (list_den A) (@list_fix A).
400 (** %\vspace{-.15in}% [[
401 = fun (A : Type) (f : list A -> list A) =>
402 fix F (ls : list A) : list A :=
403 match ls with
404 | nil => f nil
405 | x :: ls' => f (x :: F ls')
406 end
407 : forall A : Type, (list A -> list A) -> list A -> list A
408 ]] *)
409
199 Eval compute in fun A => map (tree_den A) (@tree_fix A). 410 Eval compute in fun A => map (tree_den A) (@tree_fix A).
411 (** %\vspace{-.15in}% [[
412 = fun (A : Type) (f : tree A -> tree A) =>
413 fix F (t : tree A) : tree A :=
414 match t with
415 | Leaf x => f (Leaf x)
416 | Node t1 t2 => f (Node (F t1) (F t2))
417 end
418 : forall A : Type, (tree A -> tree A) -> tree A -> tree A
419 ]] *)
200 420
201 Definition map_nat := map nat_den nat_fix. 421 Definition map_nat := map nat_den nat_fix.
202 Eval simpl in map_nat S 0. 422 Eval simpl in map_nat S 0.
423 (** %\vspace{-.15in}% [[
424 = 1%nat
425 : nat
426 ]] *)
427
203 Eval simpl in map_nat S 1. 428 Eval simpl in map_nat S 1.
429 (** %\vspace{-.15in}% [[
430 = 3%nat
431 : nat
432 ]] *)
433
204 Eval simpl in map_nat S 2. 434 Eval simpl in map_nat S 2.
435 (** %\vspace{-.15in}% [[
436 = 5%nat
437 : nat
438 ]] *)
205 439
206 440
207 (** * Proving Theorems about Recursive Definitions *) 441 (** * Proving Theorems about Recursive Definitions *)
442
443 (** We would like to be able to prove theorems about our generic functions. To do so, we need to establish additional well-formedness properties that must hold of pieces of evidence. *)
208 444
209 (* begin thide *) 445 (* begin thide *)
210 Section ok. 446 Section ok.
211 Variable T : Type. 447 Variable T : Type.
212 Variable dt : datatype. 448 Variable dt : datatype.
213 449
214 Variable dd : datatypeDenote T dt. 450 Variable dd : datatypeDenote T dt.
215 Variable fx : fixDenote T dt. 451 Variable fx : fixDenote T dt.
452
453 (** First, we characterize when a piece of evidence about a datatype is acceptable. The basic idea is that the type [T] should really be an inductive type with the definition given by [dd]. Semantically, inductive types are characterized by the ability to do induction on them. Therefore, we require that the usual induction principle is true, with respect to the constructors given in the encoding [dd]. *)
216 454
217 Definition datatypeDenoteOk := 455 Definition datatypeDenoteOk :=
218 forall P : T -> Prop, 456 forall P : T -> Prop,
219 (forall c (m : member c dt) (x : nonrecursive c) (r : ilist T (recursive c)), 457 (forall c (m : member c dt) (x : nonrecursive c) (r : ilist T (recursive c)),
220 (forall i : fin (recursive c), P (get r i)) 458 (forall i : fin (recursive c), P (get r i))
221 -> P ((hget dd m) x r)) 459 -> P ((hget dd m) x r))
222 -> forall v, P v. 460 -> forall v, P v.
223 461
462 (** This definition can take a while to digest. The quantifier over [m : member c dt] is considering each constructor in turn; like in normal induction principles, each constructor has an associated proof case. The expression [hget dd m] then names the constructor we have selected. After binding [m], we quantify over all possible arguments (encoded with [x] and [r]) to the constructor that [m] selects. Within each specific case, we quantify further over [i : fin (recursive c)] to consider all of our induction hypotheses, one for each recursive argument of the current constructor.
463
464 We have completed half the burden of defining side conditions. The other half comes in characterizing when a recursion scheme [fx] is valid. The natural condition is that [fx] behaves appropriately when applied to any constructor application. *)
465
224 Definition fixDenoteOk := 466 Definition fixDenoteOk :=
225 forall (R : Type) (cases : datatypeDenote R dt) 467 forall (R : Type) (cases : datatypeDenote R dt)
226 c (m : member c dt) 468 c (m : member c dt)
227 (x : nonrecursive c) (r : ilist T (recursive c)), 469 (x : nonrecursive c) (r : ilist T (recursive c)),
228 fx cases ((hget dd m) x r) 470 fx cases ((hget dd m) x r)
229 = (hget cases m) x (imap (fx cases) r). 471 = (hget cases m) x (imap (fx cases) r).
472
473 (** As for [datatypeDenoteOk], we consider all constructors and all possible arguments to them by quantifying over [m], [x], and [r]. The lefthand side of the equality that follows shows a call to the recursive function on the specific constructor application that we selected. The righthand side shows an application of the function case associated with constructor [m], applied to the non-recursive arguments and to appropriate recursive calls on the recursive arguments. *)
474
230 End ok. 475 End ok.
231 476
232 Implicit Arguments datatypeDenoteOk [T dt]. 477 (** We are now ready to prove that the [size] function we defined earlier always returns positive results. First, we establish a simple lemma. *)
233 478
234 Lemma foldr_plus : forall n (ils : ilist nat n), 479 Lemma foldr_plus : forall n (ils : ilist nat n),
235 foldr plus 1 ils > 0. 480 foldr plus 1 ils > 0.
236 induction ils; crush. 481 induction ils; crush.
237 Qed. 482 Qed.
241 (dd : datatypeDenote T dt) (fx : fixDenote T dt) 486 (dd : datatypeDenote T dt) (fx : fixDenote T dt)
242 (dok : datatypeDenoteOk dd) (fok : fixDenoteOk dd fx) 487 (dok : datatypeDenoteOk dd) (fok : fixDenoteOk dd fx)
243 (v : T), 488 (v : T),
244 size fx v > 0. 489 size fx v > 0.
245 (* begin thide *) 490 (* begin thide *)
491 unfold size; intros.
492 (** [[
493 ============================
494 fx nat
495 (hmake
496 (fun (x : constructor) (_ : nonrecursive x)
497 (r : ilist nat (recursive x)) => foldr plus 1%nat r) dt) v > 0
498
499 ]]
500
501 Our goal is an inequality over a particular call to [size], with its definition expanded. How can we proceed here? We cannot use [induction] directly, because there is no way for Coq to know that [T] is an inductive type. Instead, we need to use the induction principle encoded in our hypothesis [dok] of type [datatypeDenoteOk dd]. Let us try applying it directly.
502
503 [[
504 apply dok.
505
506 Error: Impossible to unify "datatypeDenoteOk dd" with
507 "fx nat
508 (hmake
509 (fun (x : constructor) (_ : nonrecursive x)
510 (r : ilist nat (recursive x)) => foldr plus 1%nat r) dt) v > 0".
511
512 ]]
513
514 Matching the type of [dok] with the type of our conclusion requires more than simple first-order unification, so [apply] is not up to the challenge. We can use the [pattern] tactic to get our goal into a form that makes it apparent exactly what the induction hypothesis is. *)
515
516 pattern v.
517
518 (** [[
519 ============================
520 (fun t : T =>
521 fx nat
522 (hmake
523 (fun (x : constructor) (_ : nonrecursive x)
524 (r : ilist nat (recursive x)) => foldr plus 1%nat r) dt) t > 0) v
525
526 ]] *)
527
528 apply dok; crush.
529 (** [[
530 H : forall i : fin (recursive c),
531 fx nat
532 (hmake
533 (fun (x : constructor) (_ : nonrecursive x)
534 (r : ilist nat (recursive x)) => foldr plus 1%nat r) dt)
535 (get r i) > 0
536 ============================
537 hget
538 (hmake
539 (fun (x0 : constructor) (_ : nonrecursive x0)
540 (r0 : ilist nat (recursive x0)) => foldr plus 1%nat r0) dt) m x
541 (imap
542 (fx nat
543 (hmake
544 (fun (x0 : constructor) (_ : nonrecursive x0)
545 (r0 : ilist nat (recursive x0)) =>
546 foldr plus 1%nat r0) dt)) r) > 0
547
548 ]]
549
550 An induction hypothesis [H] is generated, but we turn out not to need it for this example. We can simplify the goal using a library theorem about the composition of [hget] and [hmake]. *)
551
552 rewrite hget_hmake.
553 (** [[
554 ============================
555 foldr plus 1%nat
556 (imap
557 (fx nat
558 (hmake
559 (fun (x0 : constructor) (_ : nonrecursive x0)
560 (r0 : ilist nat (recursive x0)) =>
561 foldr plus 1%nat r0) dt)) r) > 0
562
563 ]]
564
565 The lemma we proved earlier finishes the proof. *)
566
567 apply foldr_plus.
568
569 (** Using hints, we can redo this proof in a nice automated form. *)
570
571 Restart.
572
246 Hint Rewrite hget_hmake : cpdt. 573 Hint Rewrite hget_hmake : cpdt.
247 Hint Resolve foldr_plus. 574 Hint Resolve foldr_plus.
248 575
249 unfold size; intros; pattern v; apply dok; crush. 576 unfold size; intros; pattern v; apply dok; crush.
250 Qed. 577 Qed.
251 (* end thide *) 578 (* end thide *)
579
580 (** It turned out that, in this example, we only needed to use induction degenerately as case analysis. A more involved theorem may only be proved using induction hypotheses. We will give its proof only in unautomated form and leave effective automation as an exercise for the motivated reader.
581
582 In particular, it ought to be the case that generic [map] applied to an identity function is itself an identity function. *)
252 583
253 Theorem map_id : forall T dt 584 Theorem map_id : forall T dt
254 (dd : datatypeDenote T dt) (fx : fixDenote T dt) 585 (dd : datatypeDenote T dt) (fx : fixDenote T dt)
255 (dok : datatypeDenoteOk dd) (fok : fixDenoteOk dd fx) 586 (dok : datatypeDenoteOk dd) (fok : fixDenoteOk dd fx)
256 (v : T), 587 (v : T),
257 map dd fx (fun x => x) v = v. 588 map dd fx (fun x => x) v = v.
258 (* begin thide *) 589 (* begin thide *)
590 (** Let us begin as we did in the last theorem, after adding another useful library equality as a hint. *)
591
259 Hint Rewrite hget_hmap : cpdt. 592 Hint Rewrite hget_hmap : cpdt.
260 593
261 unfold map; intros; pattern v; apply dok; crush. 594 unfold map; intros; pattern v; apply dok; crush.
262 match goal with 595 (** [[
263 | [ |- hget _ _ _ ?R1 = hget _ _ _ ?R2 ] => replace R1 with R2 596 H : forall i : fin (recursive c),
264 end; crush. 597 fx T
265 598 (hmap
266 induction (recursive c); crush. 599 (fun (x : constructor) (c : constructorDenote T x)
267 dep_destruct r; reflexivity. 600 (x0 : nonrecursive x) (r : ilist T (recursive x)) =>
268 dep_destruct r; crush. 601 c x0 r) dd) (get r i) = get r i
269 rewrite (H First). 602 ============================
603 hget dd m x
604 (imap
605 (fx T
606 (hmap
607 (fun (x0 : constructor) (c0 : constructorDenote T x0)
608 (x1 : nonrecursive x0) (r0 : ilist T (recursive x0)) =>
609 c0 x1 r0) dd)) r) = hget dd m x r
610
611 ]]
612
613 Our goal is an equality whose two sides begin with the same function call and initial arguments. We believe that the remaining arguments are in fact equal as well, and the [f_equal] tactic applies this reasoning step for us formally. *)
614
270 f_equal. 615 f_equal.
271 apply IHn; crush. 616 (** [[
617 ============================
618 imap
619 (fx T
620 (hmap
621 (fun (x0 : constructor) (c0 : constructorDenote T x0)
622 (x1 : nonrecursive x0) (r0 : ilist T (recursive x0)) =>
623 c0 x1 r0) dd)) r = r
624
625 ]]
626
627 At this point, it is helpful to proceed by an inner induction on the heterogeneous list [r] of recursive call results. We could arrive at a cleaner proof by breaking this step out into an explicit lemma, but here we will do the induction inline to save space.*)
628
629 induction r; crush.
630
631 (** The base case is discharged automatically, and the inductive case looks like this, where [H] is the outer IH (for induction over [T] values) and [IHn] is the inner IH (for induction over the recursive arguments).
632
633 [[
634 H : forall i : fin (S n),
635 fx T
636 (hmap
637 (fun (x : constructor) (c : constructorDenote T x)
638 (x0 : nonrecursive x) (r : ilist T (recursive x)) =>
639 c x0 r) dd)
640 (match i in (fin n') return ((fin (pred n') -> T) -> T) with
641 | First n => fun _ : fin n -> T => a
642 | Next n idx' => fun get_ls' : fin n -> T => get_ls' idx'
643 end (get r)) =
644 match i in (fin n') return ((fin (pred n') -> T) -> T) with
645 | First n => fun _ : fin n -> T => a
646 | Next n idx' => fun get_ls' : fin n -> T => get_ls' idx'
647 end (get r)
648 IHr : (forall i : fin n,
649 fx T
650 (hmap
651 (fun (x : constructor) (c : constructorDenote T x)
652 (x0 : nonrecursive x) (r : ilist T (recursive x)) =>
653 c x0 r) dd) (get r i) = get r i) ->
654 imap
655 (fx T
656 (hmap
657 (fun (x : constructor) (c : constructorDenote T x)
658 (x0 : nonrecursive x) (r : ilist T (recursive x)) =>
659 c x0 r) dd)) r = r
660 ============================
661 ICons
662 (fx T
663 (hmap
664 (fun (x0 : constructor) (c0 : constructorDenote T x0)
665 (x1 : nonrecursive x0) (r0 : ilist T (recursive x0)) =>
666 c0 x1 r0) dd) a)
667 (imap
668 (fx T
669 (hmap
670 (fun (x0 : constructor) (c0 : constructorDenote T x0)
671 (x1 : nonrecursive x0) (r0 : ilist T (recursive x0)) =>
672 c0 x1 r0) dd)) r) = ICons a r
673
674 ]]
675
676 We see another opportunity to apply [f_equal], this time to split our goal into two different equalities over corresponding arguments. After that, the form of the first goal matches our outer induction hypothesis [H], when we give type inference some help by specifying the right quantifier instantiation. *)
677
678 f_equal.
679 apply (H First).
680 (** [[
681 ============================
682 imap
683 (fx T
684 (hmap
685 (fun (x0 : constructor) (c0 : constructorDenote T x0)
686 (x1 : nonrecursive x0) (r0 : ilist T (recursive x0)) =>
687 c0 x1 r0) dd)) r = r
688
689 ]]
690
691 Now the goal matches the inner IH [IHr]. *)
692
693 apply IHr; crush.
694
695 (** [[
696 i : fin n
697 ============================
698 fx T
699 (hmap
700 (fun (x0 : constructor) (c0 : constructorDenote T x0)
701 (x1 : nonrecursive x0) (r0 : ilist T (recursive x0)) =>
702 c0 x1 r0) dd) (get r i) = get r i
703
704 ]]
705
706 We can finish the proof by applying the outer IH again, specialized to a different [fin] value. *)
707
272 apply (H (Next i)). 708 apply (H (Next i)).
273 Qed. 709 Qed.
274 (* end thide *) 710 (* end thide *)