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 ]] *)