comparison src/DataStruct.v @ 109:fe6cfbae86b9

Recursive type definitions
author Adam Chlipala <adamc@hcoop.net>
date Mon, 13 Oct 2008 14:04:39 -0400
parents 7abf5535baab
children 4627b9caac8b
comparison
equal deleted inserted replaced
108:7abf5535baab 109:fe6cfbae86b9
318 ]] *) 318 ]] *)
319 319
320 (** We are starting to develop the tools behind dependent typing's amazing advantage over alternative approaches in several important areas. Here, we have implemented complete syntax, typing rules, and evaluation semantics for simply-typed lambda calculus without even needing to define a syntactic substitution operation. We did it all without a single line of proof, and our implementation is manifestly executable. In a later chapter, we will meet other, more common approaches to language formalization. Such approaches often state and prove explicit theorems about type safety of languages. In the above example, we got type safety, termination, and other meta-theorems for free, by reduction to CIC, which we know has those properties. *) 320 (** We are starting to develop the tools behind dependent typing's amazing advantage over alternative approaches in several important areas. Here, we have implemented complete syntax, typing rules, and evaluation semantics for simply-typed lambda calculus without even needing to define a syntactic substitution operation. We did it all without a single line of proof, and our implementation is manifestly executable. In a later chapter, we will meet other, more common approaches to language formalization. Such approaches often state and prove explicit theorems about type safety of languages. In the above example, we got type safety, termination, and other meta-theorems for free, by reduction to CIC, which we know has those properties. *)
321 321
322 322
323 (** * Recursive Type Definitions *)
324
325 (** There is another style of datatype definition that leads to much simpler definitions of the [get] and [hget] definitions above. Because Coq supports "type-level computation," we can redo our inductive definitions as %\textit{%#<i>#recursive#</i>#%}% definitions. *)
326
327 Section filist.
328 Variable A : Set.
329
330 Fixpoint filist (n : nat) : Set :=
331 match n with
332 | O => unit
333 | S n' => A * filist n'
334 end%type.
335
336 (** We say that a list of length 0 has no contents, and a list of length [S n'] is a pair of a data value and a list of length [n']. *)
337
338 Fixpoint findex (n : nat) : Set :=
339 match n with
340 | O => Empty_set
341 | S n' => option (findex n')
342 end.
343
344 (** We express that there are no index values when [n = O], by defining such indices as type [Empty_set]; and we express that, at [n = S n'], there is a choice between picking the first element of the list (represented as [None]) or choosing a later element (represented by [Some idx], where [idx] is an index into the list tail). *)
345
346 Fixpoint fget (n : nat) : filist n -> findex n -> A :=
347 match n return filist n -> findex n -> A with
348 | O => fun _ idx => match idx with end
349 | S n' => fun ls idx =>
350 match idx with
351 | None => fst ls
352 | Some idx' => fget n' (snd ls) idx'
353 end
354 end.
355
356 (** Our new [get] implementation needs only one dependent [match], which just copies the stated return type of the function. Our choices of data structure implementations lead to just the right typing behavior for this new definition to work out. *)
357 End filist.
358
359 (** Heterogeneous lists are a little trickier to define with recursion, but we then reap similar benefits in simplicity of use. *)
360
361 Section fhlist.
362 Variable A : Type.
363 Variable B : A -> Type.
364
365 Fixpoint fhlist (ls : list A) : Type :=
366 match ls with
367 | nil => unit
368 | x :: ls' => B x * fhlist ls'
369 end%type.
370
371 (** The definition of [fhlist] follows the definition of [filist], with the added wrinkle of dependently-typed data elements. *)
372
373 Variable elm : A.
374
375 Fixpoint fmember (ls : list A) : Type :=
376 match ls with
377 | nil => Empty_set
378 | x :: ls' => (x = elm) + fmember ls'
379 end%type.
380
381 (** The definition of [fmember] follows the definition of [findex]. Empty lists have no members, and member types for nonempty lists are built by adding one new option to the type of members of the list tail. While for [index] we needed no new information associated with the option that we add, here we need to know that the head of the list equals the element we are searching for. We express that with a sum type whose left branch is the appropriate equality proposition. Since we define [fmember] to live in [Type], we can insert [Prop] types as needed, because [Prop] is a subtype of [Type].
382
383 We know all of the tricks needed to write a first attempt at a [get] function for [fhlist]s.
384
385 [[
386
387 Fixpoint fhget (ls : list A) : fhlist ls -> fmember ls -> B elm :=
388 match ls return fhlist ls -> fmember ls -> B elm with
389 | nil => fun _ idx => match idx with end
390 | _ :: ls' => fun mls idx =>
391 match idx with
392 | inl _ => fst mls
393 | inr idx' => fhget ls' (snd mls) idx'
394 end
395 end.
396
397 Only one problem remains. The expression [fst mls] is not known to have the proper type. To demonstrate that it does, we need to use the proof available in the [inl] case of the inner [match]. *)
398
399 Fixpoint fhget (ls : list A) : fhlist ls -> fmember ls -> B elm :=
400 match ls return fhlist ls -> fmember ls -> B elm with
401 | nil => fun _ idx => match idx with end
402 | _ :: ls' => fun mls idx =>
403 match idx with
404 | inl pf => match pf with
405 | refl_equal => fst mls
406 end
407 | inr idx' => fhget ls' (snd mls) idx'
408 end
409 end.
410
411 (** By pattern-matching on the equality proof [pf], we make that equality known to the type-checker. Exactly why this works can be seen by studying the definition of equality. *)
412
413 Print eq.
414 (** [[
415
416 Inductive eq (A : Type) (x : A) : A -> Prop := refl_equal : x = x
417 ]]
418
419 In a proposition [x = y], we see that [x] is a parameter and [y] is a regular argument. The type of the constructor [refl_equal] shows that [y] can only ever be instantiated to [x]. Thus, within a pattern-match with [refl_equal], occurrences of [y] can be replaced with occurrences of [x] for typing purposes. All examples of similar dependent pattern matching that we have seen before require explicit annotations, but Coq implements a special case of annotation inference for matches on equality proofs. *)
420 End fhlist.