adamc@222: (* Copyright (c) 2008-2009, Adam Chlipala adamc@158: * adamc@158: * This work is licensed under a adamc@158: * Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 adamc@158: * Unported License. adamc@158: * The license text is available at: adamc@158: * http://creativecommons.org/licenses/by-nc-nd/3.0/ adamc@158: *) adamc@158: adamc@158: (* begin hide *) adamc@168: Require Import Eqdep String List. adamc@158: adamc@168: Require Import Axioms Tactics. adamc@158: adamc@158: Set Implicit Arguments. adamc@158: (* end hide *) adamc@158: adamc@158: adamc@158: (** %\chapter{Higher-Order Abstract Syntax}% *) adamc@158: adamc@158: (** TODO: Prose for this chapter *) adamc@158: adamc@158: adamc@158: (** * Parametric Higher-Order Abstract Syntax *) adamc@158: adamc@158: Inductive type : Type := adamc@159: | Nat : type adamc@158: | Arrow : type -> type -> type. adamc@158: adamc@158: Infix "-->" := Arrow (right associativity, at level 60). adamc@158: adamc@158: Section exp. adamc@158: Variable var : type -> Type. adamc@158: adamc@158: Inductive exp : type -> Type := adamc@159: | Const' : nat -> exp Nat adamc@159: | Plus' : exp Nat -> exp Nat -> exp Nat adamc@159: adamc@158: | Var : forall t, var t -> exp t adamc@158: | App' : forall dom ran, exp (dom --> ran) -> exp dom -> exp ran adamc@158: | Abs' : forall dom ran, (var dom -> exp ran) -> exp (dom --> ran). adamc@158: End exp. adamc@158: adamc@158: Implicit Arguments Const' [var]. adamc@158: Implicit Arguments Var [var t]. adamc@158: Implicit Arguments Abs' [var dom ran]. adamc@158: adamc@158: Definition Exp t := forall var, exp var t. adamc@169: (* begin thide *) adamc@158: Definition Exp1 t1 t2 := forall var, var t1 -> exp var t2. adamc@158: adamc@159: Definition Const (n : nat) : Exp Nat := adamc@159: fun _ => Const' n. adamc@159: Definition Plus (E1 E2 : Exp Nat) : Exp Nat := adamc@159: fun _ => Plus' (E1 _) (E2 _). adamc@158: Definition App dom ran (F : Exp (dom --> ran)) (X : Exp dom) : Exp ran := adamc@158: fun _ => App' (F _) (X _). adamc@158: Definition Abs dom ran (B : Exp1 dom ran) : Exp (dom --> ran) := adamc@158: fun _ => Abs' (B _). adamc@169: (* end thide *) adamc@169: adamc@169: (* EX: Define appropriate shorthands, so that these definitions type-check. *) adamc@158: adamc@166: Definition zero := Const 0. adamc@166: Definition one := Const 1. adamc@166: Definition one_again := Plus zero one. adamc@166: Definition ident : Exp (Nat --> Nat) := Abs (fun _ X => Var X). adamc@166: Definition app_ident := App ident one_again. adamc@166: Definition app : Exp ((Nat --> Nat) --> Nat --> Nat) := fun _ => adamc@166: Abs' (fun f => Abs' (fun x => App' (Var f) (Var x))). adamc@166: Definition app_ident' := App (App app ident) one_again. adamc@166: adamc@169: (* EX: Define a function to count the number of variable occurrences in an [Exp]. *) adamc@169: adamc@169: (* begin thide *) adamc@222: Fixpoint countVars t (e : exp (fun _ => unit) t) : nat := adamc@166: match e with adamc@166: | Const' _ => 0 adamc@166: | Plus' e1 e2 => countVars e1 + countVars e2 adamc@166: | Var _ _ => 1 adamc@166: | App' _ _ e1 e2 => countVars e1 + countVars e2 adamc@166: | Abs' _ _ e' => countVars (e' tt) adamc@166: end. adamc@166: adamc@166: Definition CountVars t (E : Exp t) : nat := countVars (E _). adamc@169: (* end thide *) adamc@166: adamc@166: Eval compute in CountVars zero. adamc@166: Eval compute in CountVars one. adamc@166: Eval compute in CountVars one_again. adamc@166: Eval compute in CountVars ident. adamc@166: Eval compute in CountVars app_ident. adamc@166: Eval compute in CountVars app. adamc@166: Eval compute in CountVars app_ident'. adamc@166: adamc@169: (* EX: Define a function to count the number of occurrences of a single distinguished variable. *) adamc@169: adamc@169: (* begin thide *) adamc@222: Fixpoint countOne t (e : exp (fun _ => bool) t) : nat := adamc@166: match e with adamc@166: | Const' _ => 0 adamc@166: | Plus' e1 e2 => countOne e1 + countOne e2 adamc@166: | Var _ true => 1 adamc@166: | Var _ false => 0 adamc@166: | App' _ _ e1 e2 => countOne e1 + countOne e2 adamc@166: | Abs' _ _ e' => countOne (e' false) adamc@166: end. adamc@166: adamc@166: Definition CountOne t1 t2 (E : Exp1 t1 t2) : nat := adamc@166: countOne (E _ true). adamc@169: (* end thide *) adamc@166: adamc@166: Definition ident1 : Exp1 Nat Nat := fun _ X => Var X. adamc@166: Definition add_self : Exp1 Nat Nat := fun _ X => Plus' (Var X) (Var X). adamc@166: Definition app_zero : Exp1 (Nat --> Nat) Nat := fun _ X => App' (Var X) (Const' 0). adamc@166: Definition app_ident1 : Exp1 Nat Nat := fun _ X => App' (Abs' (fun Y => Var Y)) (Var X). adamc@166: adamc@166: Eval compute in CountOne ident1. adamc@166: Eval compute in CountOne add_self. adamc@166: Eval compute in CountOne app_zero. adamc@166: Eval compute in CountOne app_ident1. adamc@166: adamc@169: (* EX: Define a function to pretty-print [Exp]s as strings. *) adamc@169: adamc@169: (* begin thide *) adamc@166: Section ToString. adamc@166: Open Scope string_scope. adamc@166: adamc@166: Fixpoint natToString (n : nat) : string := adamc@166: match n with adamc@166: | O => "O" adamc@166: | S n' => "S(" ++ natToString n' ++ ")" adamc@166: end. adamc@166: adamc@222: Fixpoint toString t (e : exp (fun _ => string) t) (cur : string) : string * string := adamc@166: match e with adamc@166: | Const' n => (cur, natToString n) adamc@166: | Plus' e1 e2 => adamc@166: let (cur', s1) := toString e1 cur in adamc@166: let (cur'', s2) := toString e2 cur' in adamc@166: (cur'', "(" ++ s1 ++ ") + (" ++ s2 ++ ")") adamc@166: | Var _ s => (cur, s) adamc@166: | App' _ _ e1 e2 => adamc@166: let (cur', s1) := toString e1 cur in adamc@166: let (cur'', s2) := toString e2 cur' in adamc@166: (cur'', "(" ++ s1 ++ ") (" ++ s2 ++ ")") adamc@166: | Abs' _ _ e' => adamc@166: let (cur', s) := toString (e' cur) (cur ++ "'") in adamc@166: (cur', "(\" ++ cur ++ ", " ++ s ++ ")") adamc@166: end. adamc@166: adamc@166: Definition ToString t (E : Exp t) : string := snd (toString (E _) "x"). adamc@166: End ToString. adamc@169: (* end thide *) adamc@166: adamc@166: Eval compute in ToString zero. adamc@166: Eval compute in ToString one. adamc@166: Eval compute in ToString one_again. adamc@166: Eval compute in ToString ident. adamc@166: Eval compute in ToString app_ident. adamc@166: Eval compute in ToString app. adamc@166: Eval compute in ToString app_ident'. adamc@166: adamc@169: (* EX: Define a substitution function. *) adamc@169: adamc@169: (* begin thide *) adamc@158: Section flatten. adamc@158: Variable var : type -> Type. adamc@158: adamc@222: Fixpoint flatten t (e : exp (exp var) t) : exp var t := adamc@222: match e with adamc@159: | Const' n => Const' n adamc@159: | Plus' e1 e2 => Plus' (flatten e1) (flatten e2) adamc@158: | Var _ e' => e' adamc@158: | App' _ _ e1 e2 => App' (flatten e1) (flatten e2) adamc@158: | Abs' _ _ e' => Abs' (fun x => flatten (e' (Var x))) adamc@158: end. adamc@158: End flatten. adamc@158: adamc@158: Definition Subst t1 t2 (E1 : Exp t1) (E2 : Exp1 t1 t2) : Exp t2 := fun _ => adamc@158: flatten (E2 _ (E1 _)). adamc@169: (* end thide *) adamc@158: adamc@166: Eval compute in Subst one ident1. adamc@166: Eval compute in Subst one add_self. adamc@166: Eval compute in Subst ident app_zero. adamc@166: Eval compute in Subst one app_ident1. adamc@166: adamc@158: adamc@158: (** * A Type Soundness Proof *) adamc@158: adamc@158: Reserved Notation "E1 ==> E2" (no associativity, at level 90). adamc@158: adamc@158: Inductive Val : forall t, Exp t -> Prop := adamc@159: | VConst : forall n, Val (Const n) adamc@158: | VAbs : forall dom ran (B : Exp1 dom ran), Val (Abs B). adamc@158: adamc@158: Hint Constructors Val. adamc@158: adamc@162: Inductive Ctx : type -> type -> Type := adamc@162: | AppCong1 : forall (dom ran : type), adamc@162: Exp dom -> Ctx (dom --> ran) ran adamc@162: | AppCong2 : forall (dom ran : type), adamc@162: Exp (dom --> ran) -> Ctx dom ran adamc@162: | PlusCong1 : Exp Nat -> Ctx Nat Nat adamc@162: | PlusCong2 : Exp Nat -> Ctx Nat Nat. adamc@162: adamc@162: Inductive isCtx : forall t1 t2, Ctx t1 t2 -> Prop := adamc@162: | IsApp1 : forall dom ran (X : Exp dom), isCtx (AppCong1 ran X) adamc@162: | IsApp2 : forall dom ran (F : Exp (dom --> ran)), Val F -> isCtx (AppCong2 F) adamc@162: | IsPlus1 : forall E2, isCtx (PlusCong1 E2) adamc@162: | IsPlus2 : forall E1, Val E1 -> isCtx (PlusCong2 E1). adamc@162: adamc@162: Definition plug t1 t2 (C : Ctx t1 t2) : Exp t1 -> Exp t2 := adamc@222: match C with adamc@162: | AppCong1 _ _ X => fun F => App F X adamc@162: | AppCong2 _ _ F => fun X => App F X adamc@162: | PlusCong1 E2 => fun E1 => Plus E1 E2 adamc@162: | PlusCong2 E1 => fun E2 => Plus E1 E2 adamc@162: end. adamc@162: adamc@162: Infix "@" := plug (no associativity, at level 60). adamc@162: adamc@158: Inductive Step : forall t, Exp t -> Exp t -> Prop := adamc@158: | Beta : forall dom ran (B : Exp1 dom ran) (X : Exp dom), adamc@160: Val X adamc@160: -> App (Abs B) X ==> Subst X B adamc@159: | Sum : forall n1 n2, adamc@159: Plus (Const n1) (Const n2) ==> Const (n1 + n2) adamc@162: | Cong : forall t t' (C : Ctx t t') E E' E1, adamc@162: isCtx C adamc@162: -> E1 = C @ E adamc@162: -> E ==> E' adamc@162: -> E1 ==> C @ E' adamc@159: adamc@158: where "E1 ==> E2" := (Step E1 E2). adamc@158: adamc@162: Hint Constructors isCtx Step. adamc@158: adamc@169: (* EX: Prove type soundness. *) adamc@169: adamc@169: (* begin thide *) adamc@158: Inductive Closed : forall t, Exp t -> Prop := adamc@164: | CConst : forall n, adamc@164: Closed (Const n) adamc@159: | CPlus : forall E1 E2, adamc@159: Closed E1 adamc@159: -> Closed E2 adamc@159: -> Closed (Plus E1 E2) adamc@158: | CApp : forall dom ran (E1 : Exp (dom --> ran)) E2, adamc@158: Closed E1 adamc@158: -> Closed E2 adamc@158: -> Closed (App E1 E2) adamc@158: | CAbs : forall dom ran (E1 : Exp1 dom ran), adamc@158: Closed (Abs E1). adamc@158: adamc@158: Axiom closed : forall t (E : Exp t), Closed E. adamc@158: adamc@160: Ltac my_crush' := adamc@167: crush; adamc@158: repeat (match goal with adamc@158: | [ H : _ |- _ ] => generalize (inj_pairT2 _ _ _ _ _ H); clear H adamc@167: end; crush). adamc@160: adamc@162: Hint Extern 1 (_ = _ @ _) => simpl. adamc@162: adamc@158: Lemma progress' : forall t (E : Exp t), adamc@158: Closed E adamc@158: -> Val E \/ exists E', E ==> E'. adamc@158: induction 1; crush; adamc@159: repeat match goal with adamc@167: | [ H : Val _ |- _ ] => inversion H; []; clear H; my_crush' adamc@162: end; eauto 6. adamc@158: Qed. adamc@158: adamc@158: Theorem progress : forall t (E : Exp t), adamc@158: Val E \/ exists E', E ==> E'. adamc@158: intros; apply progress'; apply closed. adamc@158: Qed. adamc@169: (* end thide *) adamc@158: adamc@160: adamc@160: (** * Big-Step Semantics *) adamc@160: adamc@160: Reserved Notation "E1 ===> E2" (no associativity, at level 90). adamc@160: adamc@160: Inductive BigStep : forall t, Exp t -> Exp t -> Prop := adamc@160: | SConst : forall n, adamc@160: Const n ===> Const n adamc@160: | SPlus : forall E1 E2 n1 n2, adamc@160: E1 ===> Const n1 adamc@160: -> E2 ===> Const n2 adamc@160: -> Plus E1 E2 ===> Const (n1 + n2) adamc@160: adamc@160: | SApp : forall dom ran (E1 : Exp (dom --> ran)) E2 B V2 V, adamc@160: E1 ===> Abs B adamc@160: -> E2 ===> V2 adamc@160: -> Subst V2 B ===> V adamc@160: -> App E1 E2 ===> V adamc@160: | SAbs : forall dom ran (B : Exp1 dom ran), adamc@160: Abs B ===> Abs B adamc@160: adamc@160: where "E1 ===> E2" := (BigStep E1 E2). adamc@160: adamc@160: Hint Constructors BigStep. adamc@160: adamc@169: (* EX: Prove the equivalence of the small- and big-step semantics. *) adamc@169: adamc@169: (* begin thide *) adamc@160: Reserved Notation "E1 ==>* E2" (no associativity, at level 90). adamc@160: adamc@160: Inductive MultiStep : forall t, Exp t -> Exp t -> Prop := adamc@160: | Done : forall t (E : Exp t), E ==>* E adamc@160: | OneStep : forall t (E E' E'' : Exp t), adamc@160: E ==> E' adamc@160: -> E' ==>* E'' adamc@160: -> E ==>* E'' adamc@160: adamc@160: where "E1 ==>* E2" := (MultiStep E1 E2). adamc@160: adamc@160: Hint Constructors MultiStep. adamc@160: adamc@160: Theorem MultiStep_trans : forall t (E1 E2 E3 : Exp t), adamc@160: E1 ==>* E2 adamc@160: -> E2 ==>* E3 adamc@160: -> E1 ==>* E3. adamc@160: induction 1; eauto. adamc@160: Qed. adamc@160: adamc@160: Theorem Big_Val : forall t (E V : Exp t), adamc@160: E ===> V adamc@160: -> Val V. adamc@160: induction 1; crush. adamc@160: Qed. adamc@160: adamc@160: Theorem Val_Big : forall t (V : Exp t), adamc@160: Val V adamc@160: -> V ===> V. adamc@160: destruct 1; crush. adamc@160: Qed. adamc@160: adamc@160: Hint Resolve Big_Val Val_Big. adamc@160: adamc@162: Lemma Multi_Cong : forall t t' (C : Ctx t t'), adamc@162: isCtx C adamc@162: -> forall E E', E ==>* E' adamc@162: -> C @ E ==>* C @ E'. adamc@160: induction 2; crush; eauto. adamc@160: Qed. adamc@160: adamc@162: Lemma Multi_Cong' : forall t t' (C : Ctx t t') E1 E2 E E', adamc@162: isCtx C adamc@162: -> E1 = C @ E adamc@162: -> E2 = C @ E' adamc@162: -> E ==>* E' adamc@162: -> E1 ==>* E2. adamc@162: crush; apply Multi_Cong; auto. adamc@162: Qed. adamc@162: adamc@162: Hint Resolve Multi_Cong'. adamc@162: adamc@162: Ltac mtrans E := adamc@162: match goal with adamc@162: | [ |- E ==>* _ ] => fail 1 adamc@162: | _ => apply MultiStep_trans with E; [ solve [ eauto ] | eauto ] adamc@162: end. adamc@160: adamc@160: Theorem Big_Multi : forall t (E V : Exp t), adamc@160: E ===> V adamc@160: -> E ==>* V. adamc@162: induction 1; crush; eauto; adamc@162: repeat match goal with adamc@162: | [ n1 : _, E2 : _ |- _ ] => mtrans (Plus (Const n1) E2) adamc@162: | [ n1 : _, n2 : _ |- _ ] => mtrans (Plus (Const n1) (Const n2)) adamc@162: | [ B : _, E2 : _ |- _ ] => mtrans (App (Abs B) E2) adamc@162: end. adamc@160: Qed. adamc@160: adamc@160: Lemma Big_Val' : forall t (V1 V2 : Exp t), adamc@160: Val V2 adamc@160: -> V1 = V2 adamc@160: -> V1 ===> V2. adamc@160: crush. adamc@160: Qed. adamc@160: adamc@160: Hint Resolve Big_Val'. adamc@160: adamc@167: Ltac equate_conj F G := adamc@167: match constr:(F, G) with adamc@167: | (_ ?x1, _ ?x2) => constr:(x1 = x2) adamc@167: | (_ ?x1 ?y1, _ ?x2 ?y2) => constr:(x1 = x2 /\ y1 = y2) adamc@167: | (_ ?x1 ?y1 ?z1, _ ?x2 ?y2 ?z2) => constr:(x1 = x2 /\ y1 = y2 /\ z1 = z2) adamc@222: | (_ ?x1 ?y1 ?z1 ?u1, _ ?x2 ?y2 ?z2 ?u2) => adamc@222: constr:(x1 = x2 /\ y1 = y2 /\ z1 = z2 /\ u1 = u2) adamc@222: | (_ ?x1 ?y1 ?z1 ?u1 ?v1, _ ?x2 ?y2 ?z2 ?u2 ?v2) => adamc@222: constr:(x1 = x2 /\ y1 = y2 /\ z1 = z2 /\ u1 = u2 /\ v1 = v2) adamc@167: end. adamc@167: adamc@167: Ltac my_crush := adamc@167: my_crush'; adamc@167: repeat (match goal with adamc@167: | [ H : ?F = ?G |- _ ] => adamc@167: (let H' := fresh "H'" in adamc@167: assert (H' : F (fun _ => unit) = G (fun _ => unit)); [ congruence adamc@167: | discriminate || injection H'; clear H' ]; adamc@167: my_crush'; adamc@167: repeat match goal with adamc@167: | [ H : context[fun _ => unit] |- _ ] => clear H adamc@167: end; adamc@167: match type of H with adamc@167: | ?F = ?G => adamc@167: let ec := equate_conj F G in adamc@167: let var := fresh "var" in adamc@167: assert ec; [ intuition; unfold Exp; apply ext_eq; intro var; adamc@167: assert (H' : F var = G var); try congruence; adamc@167: match type of H' with adamc@167: | ?X = ?Y => adamc@167: let X := eval hnf in X in adamc@167: let Y := eval hnf in Y in adamc@167: change (X = Y) in H' adamc@167: end; injection H'; my_crush'; tauto adamc@167: | intuition; subst ] adamc@167: end); adamc@167: clear H adamc@167: end; my_crush'); adamc@167: my_crush'. adamc@167: adamc@160: Lemma Multi_Big' : forall t (E E' : Exp t), adamc@160: E ==> E' adamc@160: -> forall E'', E' ===> E'' adamc@160: -> E ===> E''. adamc@160: induction 1; crush; eauto; adamc@160: match goal with adamc@160: | [ H : _ ===> _ |- _ ] => inversion H; my_crush; eauto adamc@162: end; adamc@162: match goal with adamc@162: | [ H : isCtx _ |- _ ] => inversion H; my_crush; eauto adamc@160: end. adamc@160: Qed. adamc@160: adamc@160: Hint Resolve Multi_Big'. adamc@160: adamc@160: Theorem Multi_Big : forall t (E V : Exp t), adamc@160: E ==>* V adamc@160: -> Val V adamc@160: -> E ===> V. adamc@160: induction 1; crush; eauto. adamc@160: Qed. adamc@169: (* end thide *)