Mercurial > cpdt > repo
comparison src/Subset.v @ 75:ec2c1830a7a1
Type-checking example, with discussion
author | Adam Chlipala <adamc@hcoop.net> |
---|---|
date | Fri, 03 Oct 2008 15:10:30 -0400 |
parents | a21447f76aad |
children | 82a2189fa283 |
comparison
equal
deleted
inserted
replaced
74:a21447f76aad | 75:ec2c1830a7a1 |
---|---|
514 Defined. | 514 Defined. |
515 | 515 |
516 | 516 |
517 (** * A Type-Checking Example *) | 517 (** * A Type-Checking Example *) |
518 | 518 |
519 (** We can apply these specification types to build a certified type-checker for a simple expression language. *) | |
520 | |
519 Inductive exp : Set := | 521 Inductive exp : Set := |
520 | Nat : nat -> exp | 522 | Nat : nat -> exp |
521 | Plus : exp -> exp -> exp | 523 | Plus : exp -> exp -> exp |
522 | Bool : bool -> exp | 524 | Bool : bool -> exp |
523 | And : exp -> exp -> exp. | 525 | And : exp -> exp -> exp. |
526 | |
527 (** We define a simple language of types and its typing rules, in the style introduced in Chapter 4. *) | |
524 | 528 |
525 Inductive type : Set := TNat | TBool. | 529 Inductive type : Set := TNat | TBool. |
526 | 530 |
527 Inductive hasType : exp -> type -> Prop := | 531 Inductive hasType : exp -> type -> Prop := |
528 | HtNat : forall n, | 532 | HtNat : forall n, |
536 | HtAnd : forall e1 e2, | 540 | HtAnd : forall e1 e2, |
537 hasType e1 TBool | 541 hasType e1 TBool |
538 -> hasType e2 TBool | 542 -> hasType e2 TBool |
539 -> hasType (And e1 e2) TBool. | 543 -> hasType (And e1 e2) TBool. |
540 | 544 |
541 Definition eq_type_dec : forall (t1 t2 : type), {t1 = t2} + {t1 <> t2}. | 545 (** It will be helpful to have a function for comparing two types. We build one using [decide equality]. *) |
546 | |
547 Definition eq_type_dec : forall t1 t2 : type, {t1 = t2} + {t1 <> t2}. | |
542 decide equality. | 548 decide equality. |
543 Defined. | 549 Defined. |
550 | |
551 (** Another notation complements the monadic notation for [maybe] that we defined earlier. Sometimes we want to be to include "assertions" in our procedures. That is, we want to run a decision procedure and fail if it fails; otherwise, we want to continue, with the proof that it produced made available to us. This infix notation captures that, for a procedure that returns an arbitrary two-constructor type. *) | |
544 | 552 |
545 Notation "e1 ;; e2" := (if e1 then e2 else ??) | 553 Notation "e1 ;; e2" := (if e1 then e2 else ??) |
546 (right associativity, at level 60). | 554 (right associativity, at level 60). |
555 | |
556 (** With that notation defined, we can implement a [typeCheck] function, whose code is only more complex than what we would write in ML because it needs to include some extra type annotations. Every [[[e]]] expression adds a [hasType] proof obligation, and [crush] makes short work of them when we add [hasType]'s constructors as hints. *) | |
547 | 557 |
548 Definition typeCheck (e : exp) : {{t | hasType e t}}. | 558 Definition typeCheck (e : exp) : {{t | hasType e t}}. |
549 Hint Constructors hasType. | 559 Hint Constructors hasType. |
550 | 560 |
551 refine (fix F (e : exp) : {{t | hasType e t}} := | 561 refine (fix F (e : exp) : {{t | hasType e t}} := |
565 eq_type_dec t2 TBool;; | 575 eq_type_dec t2 TBool;; |
566 [[TBool]] | 576 [[TBool]] |
567 end); crush. | 577 end); crush. |
568 Defined. | 578 Defined. |
569 | 579 |
580 (** Despite manipulating proofs, our type checker is easy to run. *) | |
581 | |
570 Eval simpl in typeCheck (Nat 0). | 582 Eval simpl in typeCheck (Nat 0). |
583 (** [[ | |
584 | |
585 = [[TNat]] | |
586 : {{t | hasType (Nat 0) t}} | |
587 ]] *) | |
588 | |
571 Eval simpl in typeCheck (Plus (Nat 1) (Nat 2)). | 589 Eval simpl in typeCheck (Plus (Nat 1) (Nat 2)). |
590 (** [[ | |
591 | |
592 = [[TNat]] | |
593 : {{t | hasType (Plus (Nat 1) (Nat 2)) t}} | |
594 ]] *) | |
595 | |
572 Eval simpl in typeCheck (Plus (Nat 1) (Bool false)). | 596 Eval simpl in typeCheck (Plus (Nat 1) (Bool false)). |
597 (** [[ | |
598 | |
599 = ?? | |
600 : {{t | hasType (Plus (Nat 1) (Bool false)) t}} | |
601 ]] *) | |
602 | |
603 (** The type-checker also extracts to some reasonable OCaml code. *) | |
604 | |
605 Extraction typeCheck. | |
606 | |
607 (** %\begin{verbatim} | |
608 (** val typeCheck : exp -> type0 maybe **) | |
609 | |
610 let rec typeCheck = function | |
611 | Nat n -> Found TNat | |
612 | Plus (e1, e2) -> | |
613 (match typeCheck e1 with | |
614 | Unknown -> Unknown | |
615 | Found t1 -> | |
616 (match typeCheck e2 with | |
617 | Unknown -> Unknown | |
618 | Found t2 -> | |
619 (match eq_type_dec t1 TNat with | |
620 | true -> | |
621 (match eq_type_dec t2 TNat with | |
622 | true -> Found TNat | |
623 | false -> Unknown) | |
624 | false -> Unknown))) | |
625 | Bool b -> Found TBool | |
626 | And (e1, e2) -> | |
627 (match typeCheck e1 with | |
628 | Unknown -> Unknown | |
629 | Found t1 -> | |
630 (match typeCheck e2 with | |
631 | Unknown -> Unknown | |
632 | Found t2 -> | |
633 (match eq_type_dec t1 TBool with | |
634 | true -> | |
635 (match eq_type_dec t2 TBool with | |
636 | true -> Found TBool | |
637 | false -> Unknown) | |
638 | false -> Unknown))) | |
639 \end{verbatim}% | |
640 | |
641 #<pre> | |
642 (** val typeCheck : exp -> type0 maybe **) | |
643 | |
644 let rec typeCheck = function | |
645 | Nat n -> Found TNat | |
646 | Plus (e1, e2) -> | |
647 (match typeCheck e1 with | |
648 | Unknown -> Unknown | |
649 | Found t1 -> | |
650 (match typeCheck e2 with | |
651 | Unknown -> Unknown | |
652 | Found t2 -> | |
653 (match eq_type_dec t1 TNat with | |
654 | true -> | |
655 (match eq_type_dec t2 TNat with | |
656 | true -> Found TNat | |
657 | false -> Unknown) | |
658 | false -> Unknown))) | |
659 | Bool b -> Found TBool | |
660 | And (e1, e2) -> | |
661 (match typeCheck e1 with | |
662 | Unknown -> Unknown | |
663 | Found t1 -> | |
664 (match typeCheck e2 with | |
665 | Unknown -> Unknown | |
666 | Found t2 -> | |
667 (match eq_type_dec t1 TBool with | |
668 | true -> | |
669 (match eq_type_dec t2 TBool with | |
670 | true -> Found TBool | |
671 | false -> Unknown) | |
672 | false -> Unknown))) | |
673 </pre># *) | |
674 | |
675 (** %\smallskip% | |
676 | |
677 We can adapt this implementation to use [sumor], so that we know our type-checker only fails on ill-typed inputs. First, we define an analogue to the "assertion" notation. *) | |
573 | 678 |
574 Notation "e1 ;;; e2" := (if e1 then e2 else !!) | 679 Notation "e1 ;;; e2" := (if e1 then e2 else !!) |
575 (right associativity, at level 60). | 680 (right associativity, at level 60). |
576 | 681 |
577 Theorem hasType_det : forall e t1, | 682 (** Next, we prove a helpful lemma, which states that a given expression can have at most one type. *) |
683 | |
684 Lemma hasType_det : forall e t1, | |
578 hasType e t1 | 685 hasType e t1 |
579 -> forall t2, | 686 -> forall t2, |
580 hasType e t2 | 687 hasType e t2 |
581 -> t1 = t2. | 688 -> t1 = t2. |
582 induction 1; inversion 1; crush. | 689 induction 1; inversion 1; crush. |
583 Qed. | 690 Qed. |
584 | 691 |
692 (** Now we can define the type-checker. Its type expresses that it only fails on untypable expressions. *) | |
693 | |
585 Definition typeCheck' (e : exp) : {t : type | hasType e t} + {forall t, ~hasType e t}. | 694 Definition typeCheck' (e : exp) : {t : type | hasType e t} + {forall t, ~hasType e t}. |
586 Hint Constructors hasType. | 695 Hint Constructors hasType. |
696 (** We register all of the typing rules as hints. *) | |
697 | |
587 Hint Resolve hasType_det. | 698 Hint Resolve hasType_det. |
588 | 699 (** [hasType_det] will also be useful for proving proof obligations with contradictory contexts. Since its statement includes [forall]-bound variables that do not appear in its conclusion, only [eauto] will apply this hint. *) |
700 | |
701 (** Finally, the implementation of [typeCheck] can be transcribed literally, simply switching notations as needed. *) | |
589 refine (fix F (e : exp) : {t : type | hasType e t} + {forall t, ~hasType e t} := | 702 refine (fix F (e : exp) : {t : type | hasType e t} + {forall t, ~hasType e t} := |
590 match e return {t : type | hasType e t} + {forall t, ~hasType e t} with | 703 match e return {t : type | hasType e t} + {forall t, ~hasType e t} with |
591 | Nat _ => [[[TNat]]] | 704 | Nat _ => [[[TNat]]] |
592 | Plus e1 e2 => | 705 | Plus e1 e2 => |
593 t1 <-- F e1; | 706 t1 <-- F e1; |
601 t2 <-- F e2; | 714 t2 <-- F e2; |
602 eq_type_dec t1 TBool;;; | 715 eq_type_dec t1 TBool;;; |
603 eq_type_dec t2 TBool;;; | 716 eq_type_dec t2 TBool;;; |
604 [[[TBool]]] | 717 [[[TBool]]] |
605 end); clear F; crush' tt hasType; eauto. | 718 end); clear F; crush' tt hasType; eauto. |
606 Defined. | 719 |
720 (** We clear [F], the local name for the recursive function, to avoid strange proofs that refer to recursive calls that we never make. The [crush] variant [crush'] helps us by performing automatic inversion on instances of the predicates specified in its second argument. Once we throw in [eauto] to apply [hasType_det] for us, we have discharged all the subgoals. *) | |
721 Defined. | |
722 | |
723 (** The short implementation here hides just how time-saving automation is. Every use of one of the notations adds a proof obligation, giving us 12 in total. Most of these obligations require multiple inversions and either uses of [hasType_det] or applications of [hasType] rules. | |
724 | |
725 The results of simplifying calls to [typeCheck'] look deceptively similar to the results for [typeCheck], but now the types of the results provide more information. *) | |
607 | 726 |
608 Eval simpl in typeCheck' (Nat 0). | 727 Eval simpl in typeCheck' (Nat 0). |
728 (** [[ | |
729 | |
730 = [[[TNat]]] | |
731 : {t : type | hasType (Nat 0) t} + | |
732 {(forall t : type, ~ hasType (Nat 0) t)} | |
733 ]] *) | |
734 | |
609 Eval simpl in typeCheck' (Plus (Nat 1) (Nat 2)). | 735 Eval simpl in typeCheck' (Plus (Nat 1) (Nat 2)). |
736 (** [[ | |
737 | |
738 = [[[TNat]]] | |
739 : {t : type | hasType (Plus (Nat 1) (Nat 2)) t} + | |
740 {(forall t : type, ~ hasType (Plus (Nat 1) (Nat 2)) t)} | |
741 ]] *) | |
742 | |
610 Eval simpl in typeCheck' (Plus (Nat 1) (Bool false)). | 743 Eval simpl in typeCheck' (Plus (Nat 1) (Bool false)). |
744 (** [[ | |
745 | |
746 = !! | |
747 : {t : type | hasType (Plus (Nat 1) (Bool false)) t} + | |
748 {(forall t : type, ~ hasType (Plus (Nat 1) (Bool false)) t)} | |
749 ]] *) |