Mercurial > cpdt > repo
comparison src/DataStruct.v @ 112:623478074c2f
Commentary on cond example
author | Adam Chlipala <adamc@hcoop.net> |
---|---|
date | Tue, 14 Oct 2008 11:28:44 -0400 |
parents | 702e5c35cc89 |
children | ccab8a30c05e |
comparison
equal
deleted
inserted
replaced
111:702e5c35cc89 | 112:623478074c2f |
---|---|
573 | 573 |
574 (** Even if Coq would generate complete induction principles automatically for nested inductive definitions like the one we started with, there would still be advantages to using this style of reflexive encoding. We see one of those advantages in the definition of [inc], where we did not need to use any kind of auxiliary function. In general, reflexive encodings often admit direct implementations of operations that would require recursion if performed with more traditional inductive data structures. *) | 574 (** Even if Coq would generate complete induction principles automatically for nested inductive definitions like the one we started with, there would still be advantages to using this style of reflexive encoding. We see one of those advantages in the definition of [inc], where we did not need to use any kind of auxiliary function. In general, reflexive encodings often admit direct implementations of operations that would require recursion if performed with more traditional inductive data structures. *) |
575 | 575 |
576 (** ** Another Interpreter Example *) | 576 (** ** Another Interpreter Example *) |
577 | 577 |
578 Inductive type' : Type := | 578 (** We develop another example of variable-arity constructors, in the form of optimization of a small expression language with a construct like Scheme's %\texttt{%#<tt>#cond#</tt>#%}%. Each of our conditional expressions takes a list of pairs of boolean tests and bodies. The value of the conditional comes from the body of the first test in the list to evaluate to [true]. To simplify the interpreter we will write, we force each conditional to include a final, default case. *) |
579 | Nat' : type' | 579 |
580 | Bool' : type'. | 580 Inductive type' : Type := Nat | Bool. |
581 | 581 |
582 Inductive exp' : type' -> Type := | 582 Inductive exp' : type' -> Type := |
583 | NConst : nat -> exp' Nat' | 583 | NConst : nat -> exp' Nat |
584 | Plus : exp' Nat' -> exp' Nat' -> exp' Nat' | 584 | Plus : exp' Nat -> exp' Nat -> exp' Nat |
585 | Eq : exp' Nat' -> exp' Nat' -> exp' Bool' | 585 | Eq : exp' Nat -> exp' Nat -> exp' Bool |
586 | 586 |
587 | BConst : bool -> exp' Bool' | 587 | BConst : bool -> exp' Bool |
588 | Cond : forall n t, (findex n -> exp' Bool') | 588 | Cond : forall n t, (findex n -> exp' Bool) |
589 -> (findex n -> exp' t) -> exp' t -> exp' t. | 589 -> (findex n -> exp' t) -> exp' t -> exp' t. |
590 | |
591 (** A [Cond] is parameterized by a natural [n], which tells us how many cases this conditional has. The test expressions are represented with a function of type [findex n -> exp' Bool], and the bodies are represented with a function of type [findex n -> exp' t], where [t] is the overall type. The final [exp' t] argument is the default case. | |
592 | |
593 We start implementing our interpreter with a standard type denotation function. *) | |
590 | 594 |
591 Definition type'Denote (t : type') : Set := | 595 Definition type'Denote (t : type') : Set := |
592 match t with | 596 match t with |
593 | Nat' => nat | 597 | Nat => nat |
594 | Bool' => bool | 598 | Bool => bool |
595 end. | 599 end. |
600 | |
601 (** To implement the expression interpreter, it is useful to have the following function that implements the functionality of [Cond] without involving any syntax. *) | |
596 | 602 |
597 Section cond. | 603 Section cond. |
598 Variable A : Set. | 604 Variable A : Set. |
599 Variable default : A. | 605 Variable default : A. |
600 | 606 |
610 end. | 616 end. |
611 End cond. | 617 End cond. |
612 | 618 |
613 Implicit Arguments cond [A n]. | 619 Implicit Arguments cond [A n]. |
614 | 620 |
621 (** Now the expression interpreter is straightforward to write. *) | |
622 | |
615 Fixpoint exp'Denote t (e : exp' t) {struct e} : type'Denote t := | 623 Fixpoint exp'Denote t (e : exp' t) {struct e} : type'Denote t := |
616 match e in exp' t return type'Denote t with | 624 match e in exp' t return type'Denote t with |
617 | NConst n => | 625 | NConst n => |
618 n | 626 n |
619 | Plus e1 e2 => | 627 | Plus e1 e2 => |
628 (exp'Denote default) | 636 (exp'Denote default) |
629 (fun idx => exp'Denote (tests idx)) | 637 (fun idx => exp'Denote (tests idx)) |
630 (fun idx => exp'Denote (bodies idx)) | 638 (fun idx => exp'Denote (bodies idx)) |
631 end. | 639 end. |
632 | 640 |
641 (** We will implement a constant-folding function that optimizes conditionals, removing cases with known-[false] tests and cases that come after known-[true] tests. A function [cfoldCond] implements the heart of this logic. The convoy pattern is used again near the end of the implementation. *) | |
642 | |
633 Section cfoldCond. | 643 Section cfoldCond. |
634 Variable t : type'. | 644 Variable t : type'. |
635 Variable default : exp' t. | 645 Variable default : exp' t. |
636 | 646 |
637 Fixpoint cfoldCond (n : nat) : (findex n -> exp' Bool') -> (findex n -> exp' t) -> exp' t := | 647 Fixpoint cfoldCond (n : nat) |
638 match n return (findex n -> exp' Bool') -> (findex n -> exp' t) -> exp' t with | 648 : (findex n -> exp' Bool) -> (findex n -> exp' t) -> exp' t := |
649 match n return (findex n -> exp' Bool) -> (findex n -> exp' t) -> exp' t with | |
639 | O => fun _ _ => default | 650 | O => fun _ _ => default |
640 | S n' => fun tests bodies => | 651 | S n' => fun tests bodies => |
641 match tests None with | 652 match tests None with |
642 | BConst true => bodies None | 653 | BConst true => bodies None |
643 | BConst false => cfoldCond n' | 654 | BConst false => cfoldCond n' |
645 (fun idx => bodies (Some idx)) | 656 (fun idx => bodies (Some idx)) |
646 | _ => | 657 | _ => |
647 let e := cfoldCond n' | 658 let e := cfoldCond n' |
648 (fun idx => tests (Some idx)) | 659 (fun idx => tests (Some idx)) |
649 (fun idx => bodies (Some idx)) in | 660 (fun idx => bodies (Some idx)) in |
650 match e in exp' t return exp' Bool' -> exp' t -> exp' t with | 661 match e in exp' t return exp' t -> exp' t with |
651 | Cond n _ tests' bodies' default' => fun test body => | 662 | Cond n _ tests' bodies' default' => fun body => |
652 Cond | 663 Cond |
653 (S n) | 664 (S n) |
654 (fun idx => match idx with | 665 (fun idx => match idx with |
655 | None => test | 666 | None => tests None |
656 | Some idx => tests' idx | 667 | Some idx => tests' idx |
657 end) | 668 end) |
658 (fun idx => match idx with | 669 (fun idx => match idx with |
659 | None => body | 670 | None => body |
660 | Some idx => bodies' idx | 671 | Some idx => bodies' idx |
661 end) | 672 end) |
662 default' | 673 default' |
663 | e => fun test body => | 674 | e => fun body => |
664 Cond | 675 Cond |
665 1 | 676 1 |
666 (fun _ => test) | 677 (fun _ => tests None) |
667 (fun _ => body) | 678 (fun _ => body) |
668 e | 679 e |
669 end (tests None) (bodies None) | 680 end (bodies None) |
670 end | 681 end |
671 end. | 682 end. |
672 End cfoldCond. | 683 End cfoldCond. |
673 | 684 |
674 Implicit Arguments cfoldCond [t n]. | 685 Implicit Arguments cfoldCond [t n]. |
686 | |
687 (** Like for the interpreters, most of the action was in this helper function, and [cfold] itself is easy to write. *) | |
675 | 688 |
676 Fixpoint cfold t (e : exp' t) {struct e} : exp' t := | 689 Fixpoint cfold t (e : exp' t) {struct e} : exp' t := |
677 match e in exp' t return exp' t with | 690 match e in exp' t return exp' t with |
678 | NConst n => NConst n | 691 | NConst n => NConst n |
679 | Plus e1 e2 => | 692 | Plus e1 e2 => |
697 (cfold default) | 710 (cfold default) |
698 (fun idx => cfold (tests idx)) | 711 (fun idx => cfold (tests idx)) |
699 (fun idx => cfold (bodies idx)) | 712 (fun idx => cfold (bodies idx)) |
700 end. | 713 end. |
701 | 714 |
715 (** To prove our final correctness theorem, it is useful to know that [cfoldCond] preserves expression meanings. This lemma formalizes that property. The proof is a standard mostly-automated one, with the only wrinkle being a guided instantation of the quantifiers in the induction hypothesis. *) | |
716 | |
702 Lemma cfoldCond_correct : forall t (default : exp' t) | 717 Lemma cfoldCond_correct : forall t (default : exp' t) |
703 n (tests : findex n -> exp' Bool') (bodies : findex n -> exp' t), | 718 n (tests : findex n -> exp' Bool) (bodies : findex n -> exp' t), |
704 exp'Denote (cfoldCond default tests bodies) | 719 exp'Denote (cfoldCond default tests bodies) |
705 = exp'Denote (Cond n tests bodies default). | 720 = exp'Denote (Cond n tests bodies default). |
706 induction n; crush; | 721 induction n; crush; |
707 match goal with | 722 match goal with |
708 | [ IHn : forall tests bodies, _, tests : _ -> _, bodies : _ -> _ |- _ ] => | 723 | [ IHn : forall tests bodies, _, tests : _ -> _, bodies : _ -> _ |- _ ] => |
719 end] ] => dep_destruct E | 734 end] ] => dep_destruct E |
720 | [ |- context[if ?B then _ else _] ] => destruct B | 735 | [ |- context[if ?B then _ else _] ] => destruct B |
721 end; crush). | 736 end; crush). |
722 Qed. | 737 Qed. |
723 | 738 |
739 (** It is also useful to know that the result of a call to [cond] is not changed by substituting new tests and bodies functions, so long as the new functions have the same input-output behavior as the old. It turns out that, in Coq, it is not possible to prove in general that functions related in this way are equal. We treat this issue with our discussion of axioms in a later chapter. For now, it suffices to prove that the particular function [cond] is %\textit{%#<i>#extensional#</i>#%}%; that is, it is unaffected by substitution of functions with input-output equivalents. *) | |
740 | |
724 Lemma cond_ext : forall (A : Set) (default : A) n (tests tests' : findex n -> bool) | 741 Lemma cond_ext : forall (A : Set) (default : A) n (tests tests' : findex n -> bool) |
725 (bodies bodies' : findex n -> A), | 742 (bodies bodies' : findex n -> A), |
726 (forall idx, tests idx = tests' idx) | 743 (forall idx, tests idx = tests' idx) |
727 -> (forall idx, bodies idx = bodies' idx) | 744 -> (forall idx, bodies idx = bodies' idx) |
728 -> cond default tests bodies | 745 -> cond default tests bodies |
731 match goal with | 748 match goal with |
732 | [ |- context[if ?E then _ else _] ] => destruct E | 749 | [ |- context[if ?E then _ else _] ] => destruct E |
733 end; crush. | 750 end; crush. |
734 Qed. | 751 Qed. |
735 | 752 |
753 (** Now the final theorem is easy to prove. We add our two lemmas as hints and perform standard automation with pattern-matching of subterms to destruct. *) | |
754 | |
736 Theorem cfold_correct : forall t (e : exp' t), | 755 Theorem cfold_correct : forall t (e : exp' t), |
737 exp'Denote (cfold e) = exp'Denote e. | 756 exp'Denote (cfold e) = exp'Denote e. |
738 Hint Rewrite cfoldCond_correct : cpdt. | 757 Hint Rewrite cfoldCond_correct : cpdt. |
739 Hint Resolve cond_ext. | 758 Hint Resolve cond_ext. |
740 | 759 |