This file follows very closely its sibling CBVTyped.v
. It is recommended to read that
one first: the comments on this one will be quite more terse and focus on the differences.
As our framework is "well-typed well-scoped", when we mean untyped, we really mean
unityped, i.e., we will take the single element set as set of types. Although, the
typing rules will not be as uninteresting as it would seem. Indeed, as we adopt a
single-sided sequent-calculus style of presentation, we do give types to continuations
(formally negated types), and we thus have not one type, but two: the type of terms
β
and the type of continuations β
.
Typing contexts, terms, values, evaluation contexts and configurations work straightforwardly.
Variant ty : Type :=
| Left : ty
| Right : ty
.
Notation "β" := (Left) (at level 20) : ty_scope . Notation "β" := (Right) (at level 20) : ty_scope .Bind Scope ctx_scope with t_ctx .
Note that is is the proper pure untyped lambda calculus: in constrast with our STLC example, the lambda is not recursive and there is no unit value nor base type, only functions.
Inductive term (Ξ : t_ctx) : Type :=
| Val : val Ξ -> term Ξ
| App : term Ξ -> term Ξ -> term Ξ
with val (Ξ : t_ctx) : Type :=
| Var : Ξ β β -> val Ξ
| Lam : term (Ξ βΆβ β) -> val Ξ
.
Inductive ev_ctx (Ξ : t_ctx) : Type :=
| K0 : Ξ β β -> ev_ctx Ξ
| K1 : term Ξ -> ev_ctx Ξ -> ev_ctx Ξ
| K2 : val Ξ -> ev_ctx Ξ -> ev_ctx Ξ
.
Variant state (Ξ : t_ctx) : Type :=
| Cut : term Ξ -> ev_ctx Ξ -> state Ξ
.
We introduce the sorted family of generalized values, together with the generalized variable.
Equations val_m : t_ctx -> ty -> Type := val_m Ξ (β) := val Ξ ; val_m Ξ (β) := ev_ctx Ξ . Equations a_id {Ξ} : Ξ =[val_m]> Ξ := a_id (β) i := Var i ; a_id (β) i := K0 i .
Equations t_rename {Ξ Ξ} : Ξ β Ξ -> term Ξ -> term Ξ :=
t_rename f (Val v) := Val (v_rename f v) ;
t_rename f (App t1 t2) := App (t_rename f t1) (t_rename f t2) ;
with v_rename {Ξ Ξ} : Ξ β Ξ -> val Ξ -> val Ξ :=
v_rename f (Var i) := Var (f _ i) ;
v_rename f (Lam t) := Lam (t_rename (r_shift1 f) t) .
Equations e_rename {Ξ Ξ} : Ξ β Ξ -> ev_ctx Ξ -> ev_ctx Ξ :=
e_rename f (K0 i) := K0 (f _ i) ;
e_rename f (K1 t Ο) := K1 (t_rename f t) (e_rename f Ο) ;
e_rename f (K2 v Ο) := K2 (v_rename f v) (e_rename f Ο) .
Equations s_rename {Ξ Ξ} : Ξ β Ξ -> state Ξ -> state Ξ :=
s_rename f (Cut t Ο) := Cut (t_rename f t) (e_rename f Ο).
Equations m_rename {Ξ Ξ} : Ξ β Ξ -> val_m Ξ βα΅’ val_m Ξ :=
m_rename f (β) v := v_rename f v ;
m_rename f (β) Ο := e_rename f Ο .
Renaming an assigment on the left.
Definition a_ren {Ξ1 Ξ2 Ξ3} : Ξ1 =[val_m]> Ξ2 -> Ξ2 β Ξ3 -> Ξ1 =[val_m]> Ξ3 :=
fun f g _ i => m_rename g _ (f _ i) .
Arguments a_ren _ _ _ /.
The following bunch of notations will help us to keep the code readable:
Notation "t ββα΅£ r" := (t_rename r%asgn t) (at level 14).
Notation "v α΅₯βα΅£ r" := (v_rename r%asgn v) (at level 14).
Notation "Ο ββα΅£ r" := (e_rename r%asgn Ο) (at level 14).
Notation "v ββα΅£ r" := (m_rename r%asgn _ v) (at level 14).
Notation "s ββα΅£ r" := (s_rename r%asgn s) (at level 14).
Notation "a βα΅£ r" := (a_ren a%asgn r%asgn) (at level 14) : asgn_scope.
The weakenings we will need for substitution..
Definition t_shift1 {Ξ a} := @t_rename Ξ (Ξ βΆβ a) r_pop.
Definition v_shift1 {Ξ a} := @v_rename Ξ (Ξ βΆβ a) r_pop.
Definition m_shift1 {Ξ a} := @m_rename Ξ (Ξ βΆβ a) r_pop.
Definition a_shift1 {Ξ Ξ a} (f : Ξ =[val_m]> Ξ) :=
[ a_map f m_shift1 ,β a_id a top ]%asgn .
Equations t_subst {Ξ Ξ} : Ξ =[val_m]> Ξ -> term Ξ -> term Ξ :=
t_subst f (Val v) := Val (v_subst f v) ;
t_subst f (App t1 t2) := App (t_subst f t1) (t_subst f t2) ;
with v_subst {Ξ Ξ} : Ξ =[val_m]> Ξ -> val Ξ -> val Ξ :=
v_subst f (Var i) := f _ i ;
v_subst f (Lam t) := Lam (t_subst (a_shift1 f) t) .
Equations e_subst {Ξ Ξ} : Ξ =[val_m]> Ξ -> ev_ctx Ξ -> ev_ctx Ξ :=
e_subst f (K0 i) := f _ i ;
e_subst f (K1 t Ο) := K1 (t_subst f t) (e_subst f Ο) ;
e_subst f (K2 v Ο) := K2 (v_subst f v) (e_subst f Ο) .
Notation "t `ββ a" := (t_subst a%asgn t) (at level 30).
Notation "v `α΅₯β a" := (v_subst a%asgn v) (at level 30).
Notation "Ο `ββ a" := (e_subst a%asgn Ο) (at level 30).
Equations m_subst : val_m ββ β¦ val_m , val_m β§β :=
m_subst _ (β) v _ f := v `α΅₯β f ;
m_subst _ (β) Ο _ f := Ο `ββ f .
Equations s_subst : state ββ β¦ val_m , state β§β :=
s_subst _ (Cut t Ο) _ f := Cut (t `ββ f) (Ο `ββ f) .
We can now instanciate the substitution monoid and module structures.
#[global] Instance val_m_monoid : subst_monoid val_m := {| v_var := @a_id ; v_sub := m_subst |} . #[global] Instance state_module : subst_module val_m state := {| c_sub := s_subst |} .
Single-variable substitution.
Definition assign1 {Ξ a} v : (Ξ βΆβ a) =[val_m]> Ξ := a_id βΆβ v .
Definition t_subst1 {Ξ a} (t : term _) v := t `ββ @assign1 Ξ a v.
Notation "t /[ v ]" := (t_subst1 t v) (at level 50, left associativity).
As before we define observations (copatterns), as there are only functions and
continuations there is exactly one pattern at each type. Knowing this, we could
have made this type (obs
) disappear, but it is kept here for the sake of being
more explicit.
Variant obs : ty -> Type :=
| ORet : obs (β)
| OApp : obs (β)
.
Observation still behave as binders, returning bind a term (what we are returning) and applying binds a term (the argument) and a continuation.
Equations obs_dom {a} : obs a -> t_ctx :=
obs_dom (@ORet) := β
βΆβ β ;
obs_dom (@OApp) := β
βΆβ β βΆβ β .
Definition obs_op : Oper ty t_ctx :=
{| o_op := obs ; o_dom := @obs_dom |} .
Notation ORet' := (ORet : o_op obs_op _).
Notation OApp' := (OApp : o_op obs_op _).
We now define applying an observation with arguments to a value.
Equations obs_app {Ξ a} (v : val_m Ξ a) (p : obs a) (Ξ³ : obs_dom p =[val_m]> Ξ) : state Ξ :=
obs_app v (OApp) Ξ³ := Cut (Val v) (K2 (Ξ³ _ (pop top)) (Ξ³ _ top)) ;
obs_app Ο (ORet) Ξ³ := Cut (Val (Ξ³ _ top)) Ο .
Normal forms take the exact same form as for ULC: a head variable and an observation on it, with arguments.
Definition nf := c_var β₯β (obs_op # val_m).
The evaluator as a state machine.
β¨ t1 t2 | Ο β© β β¨ t2 | t1 β
1 Ο β©
β¨ v | x β©
normalβ¨ v | t β
1 Ο β© β β¨ t | v β
2 Ο β©
β¨ x | v β
2 Ο β©
normalβ¨ Ξ»x.t | v β
2 Ο β© β β¨ t[xβ¦v] | Ο β©
Rules 1,3,5 step to a new configuration, while cases 2,4 are stuck on normal forms.
Equations eval_step {Ξ : t_ctx} : state Ξ -> (state Ξ + nf Ξ) :=
eval_step (Cut (App t1 t2) (Ο)) := inl (Cut t2 (K1 t1 Ο)) ;
eval_step (Cut (Val v) (K0 i)) := inr (i β
ORet' β¦ ! βΆβ v β¦) ;
eval_step (Cut (Val v) (K1 t Ο)) := inl (Cut t (K2 v Ο)) ;
eval_step (Cut (Val (Var i)) (K2 v Ο)) := inr (i β
OApp' β¦ ! βΆβ v βΆβ Ο β¦) ;
eval_step (Cut (Val (Lam t)) (K2 v Ο)) := inl (Cut (t /[ v ]) Ο) .
Definition ulc_eval {Ξ : t_ctx} : state Ξ -> delay (nf Ξ)
:= iter_delay (ret_delay β eval_step).
We now tackle the basic syntactic lemmas on renaming and substitution. See you in 400 lines.
Scheme term_mut := Induction for term Sort Prop with val_mut := Induction for val Sort Prop . Record syn_ind_args (P_t : forall Ξ, term Ξ -> Prop) (P_v : forall Ξ, val Ξ -> Prop) (P_e : forall Ξ, ev_ctx Ξ -> Prop) := { ind_val {Ξ} v (_ : P_v Ξ v) : P_t Ξ (Val v) ; ind_app {Ξ} t1 (_ : P_t Ξ t1) t2 (_ : P_t Ξ t2) : P_t Ξ (App t1 t2) ; ind_var {Ξ} i : P_v Ξ (Var i) ; ind_lam {Ξ} t (_ : P_t _ t) : P_v Ξ (Lam t) ; ind_kvar {Ξ} i : P_e Ξ (K0 i) ; ind_kfun {Ξ} t (_ : P_t Ξ t) Ο (_ : P_e Ξ Ο) : P_e Ξ (K1 t Ο) ; ind_karg {Ξ} v (_ : P_v Ξ v) Ο (_ : P_e Ξ Ο) : P_e Ξ (K2 v Ο) } . Lemma term_ind_mut P_t P_v P_e (H : syn_ind_args P_t P_v P_e) Ξ t : P_t Ξ t . destruct H; now apply (term_mut P_t P_v). Qed. Lemma val_ind_mut P_t P_v P_e (H : syn_ind_args P_t P_v P_e) Ξ v : P_v Ξ v . destruct H; now apply (val_mut P_t P_v). Qed. Lemma ctx_ind_mut P_t P_v P_e (H : syn_ind_args P_t P_v P_e) Ξ Ο : P_e Ξ Ο . induction Ο. - apply (ind_kvar _ _ _ H). - apply (ind_kfun _ _ _ H); auto; apply (term_ind_mut _ _ _ H). - apply (ind_karg _ _ _ H); auto; apply (val_ind_mut _ _ _ H). Qed.
Renaming respects pointwise equality of assignments.
Definition t_ren_proper_P Ξ (t : term Ξ) : Prop :=
forall Ξ (f1 f2 : Ξ β Ξ), f1 β‘β f2 -> t ββα΅£ f1 = t ββα΅£ f2 .
Definition v_ren_proper_P Ξ (v : val Ξ) : Prop :=
forall Ξ (f1 f2 : Ξ β Ξ), f1 β‘β f2 -> v α΅₯βα΅£ f1 = v α΅₯βα΅£ f2 .
Definition e_ren_proper_P Ξ (Ο : ev_ctx Ξ) : Prop :=
forall Ξ (f1 f2 : Ξ β Ξ), f1 β‘β f2 -> Ο ββα΅£ f1 = Ο ββα΅£ f2 .
Lemma ren_proper_prf : syn_ind_args t_ren_proper_P v_ren_proper_P e_ren_proper_P.
econstructor.
all: unfold t_ren_proper_P, v_ren_proper_P, e_ren_proper_P.
all: intros; cbn; f_equal; auto.
all: apply H.
now apply r_shift1_eq.
Qed.
#[global] Instance t_ren_eq {Ξ Ξ}
: Proper (asgn_eq _ _ ==> eq ==> eq) (@t_rename Ξ Ξ).
intros f1 f2 H1 x y ->; now apply (term_ind_mut _ _ _ ren_proper_prf).
Qed.
#[global] Instance v_ren_eq {Ξ Ξ}
: Proper (asgn_eq _ _ ==> eq ==> eq) (@v_rename Ξ Ξ).
intros f1 f2 H1 x y ->; now apply (val_ind_mut _ _ _ ren_proper_prf).
Qed.
#[global] Instance e_ren_eq {Ξ Ξ}
: Proper (asgn_eq _ _ ==> eq ==> eq) (@e_rename Ξ Ξ).
intros f1 f2 H1 x y ->; now apply (ctx_ind_mut _ _ _ ren_proper_prf).
Qed.
#[global] Instance m_ren_eq {Ξ Ξ}
: Proper (asgn_eq _ _ ==> forall_relation (fun a => eq ==> eq)) (@m_rename Ξ Ξ).
intros ? ? H1 [] _ ? ->; cbn in *; now rewrite H1.
Qed.
#[global] Instance s_ren_eq {Ξ Ξ}
: Proper (asgn_eq _ _ ==> eq ==> eq) (@s_rename Ξ Ξ).
intros ? ? H1 _ x ->; destruct x; cbn; now rewrite H1.
Qed.
#[global] Instance a_ren_eq {Ξ1 Ξ2 Ξ3}
: Proper (asgn_eq _ _ ==> asgn_eq _ _ ==> asgn_eq _ _) (@a_ren Ξ1 Ξ2 Ξ3).
intros ?? H1 ?? H2 ??; apply (m_ren_eq _ _ H2), H1.
Qed.
#[global] Instance a_shift_eq {Ξ Ξ x} : Proper (asgn_eq _ _ ==> asgn_eq _ _) (@a_shift1 Ξ Ξ x).
intros ? ? H ? h; dependent elimination h; cbn; auto.
now rewrite H.
Qed.
Renaming-renaming assocativity.
Definition t_ren_ren_P Ξ1 (t : term Ξ1) : Prop :=
forall Ξ2 Ξ3 (f1 : Ξ1 β Ξ2) (f2 : Ξ2 β Ξ3),
(t ββα΅£ f1) ββα΅£ f2 = t ββα΅£ (f1 α΅£β f2) .
Definition v_ren_ren_P Ξ1 (v : val Ξ1) : Prop :=
forall Ξ2 Ξ3 (f1 : Ξ1 β Ξ2) (f2 : Ξ2 β Ξ3),
(v α΅₯βα΅£ f1) α΅₯βα΅£ f2 = v α΅₯βα΅£ (f1 α΅£β f2) .
Definition e_ren_ren_P Ξ1 (Ο : ev_ctx Ξ1) : Prop :=
forall Ξ2 Ξ3 (f1 : Ξ1 β Ξ2) (f2 : Ξ2 β Ξ3),
(Ο ββα΅£ f1) ββα΅£ f2 = Ο ββα΅£ (f1 α΅£β f2) .
Lemma ren_ren_prf : syn_ind_args t_ren_ren_P v_ren_ren_P e_ren_ren_P .
econstructor.
all: unfold t_ren_ren_P, v_ren_ren_P, e_ren_ren_P.
all: intros; cbn; f_equal; auto.
rewrite H; apply t_ren_eq; auto.
intros ? h; dependent elimination h; auto.
Qed.
Lemma t_ren_ren {Ξ1 Ξ2 Ξ3} (f1 : Ξ1 β Ξ2) (f2 : Ξ2 β Ξ3) (t : term Ξ1)
: (t ββα΅£ f1) ββα΅£ f2 = t ββα΅£ (f1 α΅£β f2) .
now apply (term_ind_mut _ _ _ ren_ren_prf).
Qed.
Lemma v_ren_ren {Ξ1 Ξ2 Ξ3} (f1 : Ξ1 β Ξ2) (f2 : Ξ2 β Ξ3) (v : val Ξ1)
: (v α΅₯βα΅£ f1) α΅₯βα΅£ f2 = v α΅₯βα΅£ (f1 α΅£β f2) .
now apply (val_ind_mut _ _ _ ren_ren_prf).
Qed.
Lemma e_ren_ren {Ξ1 Ξ2 Ξ3} (f1 : Ξ1 β Ξ2) (f2 : Ξ2 β Ξ3) (Ο : ev_ctx Ξ1)
: (Ο ββα΅£ f1) ββα΅£ f2 = Ο ββα΅£ (f1 α΅£β f2) .
now apply (ctx_ind_mut _ _ _ ren_ren_prf).
Qed.
Lemma m_ren_ren {Ξ1 Ξ2 Ξ3} (f1 : Ξ1 β Ξ2) (f2 : Ξ2 β Ξ3) a (v : val_m Ξ1 a)
: (v ββα΅£ f1) ββα΅£ f2 = v ββα΅£ (f1 α΅£β f2) .
destruct a; [ now apply v_ren_ren | now apply e_ren_ren ].
Qed.
Lemma s_ren_ren {Ξ1 Ξ2 Ξ3} (f1 : Ξ1 β Ξ2) (f2 : Ξ2 β Ξ3) (s : state Ξ1)
: (s ββα΅£ f1) ββα΅£ f2 = s ββα΅£ (f1 α΅£β f2) .
destruct s; apply (f_equal2 Cut); [ now apply t_ren_ren | now apply e_ren_ren ].
Qed.
Left identity law of renaming.
Definition t_ren_id_l_P Ξ (t : term Ξ) : Prop := t ββα΅£ r_id = t .
Definition v_ren_id_l_P Ξ (v : val Ξ) : Prop := v α΅₯βα΅£ r_id = v .
Definition e_ren_id_l_P Ξ (Ο : ev_ctx Ξ) : Prop := Ο ββα΅£ r_id = Ο .
Lemma ren_id_l_prf : syn_ind_args t_ren_id_l_P v_ren_id_l_P e_ren_id_l_P .
econstructor.
all: unfold t_ren_id_l_P, v_ren_id_l_P, e_ren_id_l_P.
all: intros; cbn; f_equal; auto.
rewrite <- H at 2; apply t_ren_eq; auto.
intros ? h; dependent elimination h; auto.
Qed.
Lemma t_ren_id_l {Ξ} (t : term Ξ) : t ββα΅£ r_id = t.
now apply (term_ind_mut _ _ _ ren_id_l_prf).
Qed.
Lemma v_ren_id_l {Ξ} (v : val Ξ) : v α΅₯βα΅£ r_id = v .
now apply (val_ind_mut _ _ _ ren_id_l_prf).
Qed.
Lemma e_ren_id_l {Ξ} (Ο : ev_ctx Ξ) : Ο ββα΅£ r_id = Ο.
now apply (ctx_ind_mut _ _ _ ren_id_l_prf).
Qed.
Lemma m_ren_id_l {Ξ a} (v : val_m Ξ a) : v ββα΅£ r_id = v .
destruct a; [ now apply v_ren_id_l | now apply e_ren_id_l ].
Qed.
Lemma s_ren_id_l {Ξ} (s : state Ξ) : s ββα΅£ r_id = s .
destruct s; apply (f_equal2 Cut); [ now apply t_ren_id_l | now apply e_ren_id_l ].
Qed.
Lemma m_ren_id_r {Ξ Ξ} (f : Ξ β Ξ) {a} (i : Ξ β a) : a_id _ i ββα΅£ f = a_id _ (f _ i) .
now destruct a.
Qed.
Lemma a_ren_id_r {Ξ Ξ} (f : Ξ β Ξ) : a_id βα΅£ f β‘β f α΅£β a_id .
intros ??; now apply m_ren_id_r.
Qed.
Lemma a_shift_id {Ξ x} : @a_shift1 Ξ Ξ x a_id β‘β a_id.
intros ? v; dependent elimination v; auto.
exact (m_ren_id_r _ _).
Qed.
Lemma 5: shifting assigments commutes with left and right renaming.
Lemma a_shift1_s_ren {Ξ1 Ξ2 Ξ3 a} (f1 : Ξ1 β Ξ2) (f2 : Ξ2 =[val_m]> Ξ3)
: @a_shift1 _ _ a (f1 α΅£β f2) β‘β r_shift1 f1 α΅£β a_shift1 f2 .
intros ? v; dependent elimination v; auto.
Qed.
Lemma a_shift1_a_ren {Ξ1 Ξ2 Ξ3 a} (f1 : Ξ1 =[val_m]> Ξ2) (f2 : Ξ2 β Ξ3)
: @a_shift1 _ _ a (f1 βα΅£ f2) β‘β a_shift1 f1 βα΅£ r_shift1 f2 .
intros ? v.
dependent elimination v.
cbn; now rewrite m_ren_id_r.
cbn; unfold m_shift1, r_shift1; now rewrite 2 m_ren_ren.
Qed.
Lemma 6: substitution respects pointwise equality of assigments.
Definition t_sub_proper_P Ξ (t : term Ξ) : Prop :=
forall Ξ (f1 f2 : Ξ =[val_m]> Ξ), f1 β‘β f2 -> t `ββ f1 = t `ββ f2 .
Definition v_sub_proper_P Ξ (v : val Ξ) : Prop :=
forall Ξ (f1 f2 : Ξ =[val_m]> Ξ), f1 β‘β f2 -> v `α΅₯β f1 = v `α΅₯β f2 .
Definition e_sub_proper_P Ξ (Ο : ev_ctx Ξ) : Prop :=
forall Ξ (f1 f2 : Ξ =[val_m]> Ξ), f1 β‘β f2 -> Ο `ββ f1 = Ο `ββ f2.
Lemma sub_proper_prf : syn_ind_args t_sub_proper_P v_sub_proper_P e_sub_proper_P.
econstructor.
all: unfold t_sub_proper_P, v_sub_proper_P, e_sub_proper_P.
all: intros; cbn; f_equal; auto.
now apply H, a_shift_eq.
Qed.
#[global] Instance t_sub_eq {Ξ Ξ}
: Proper (asgn_eq _ _ ==> eq ==> eq) (@t_subst Ξ Ξ).
intros ? ? H1 _ ? ->; now apply (term_ind_mut _ _ _ sub_proper_prf).
Qed.
#[global] Instance v_sub_eq {Ξ Ξ}
: Proper (asgn_eq _ _ ==> eq ==> eq) (@v_subst Ξ Ξ).
intros ? ? H1 _ ? ->; now apply (val_ind_mut _ _ _ sub_proper_prf).
Qed.
#[global] Instance e_sub_eq {Ξ Ξ}
: Proper (asgn_eq _ _ ==> eq ==> eq) (@e_subst Ξ Ξ).
intros ? ? H1 _ ? ->; now apply (ctx_ind_mut _ _ _ sub_proper_prf).
Qed.
#[global] Instance m_sub_eq
: Proper (ββ Ξ, ββ _, eq ==> ββ Ξ, asgn_eq Ξ Ξ ==> eq) m_subst.
intros ? [] ?? -> ??? H1; cbn in *; now rewrite H1.
Qed.
#[global] Instance s_sub_eq
: Proper (ββ Ξ, eq ==> ββ Ξ, asgn_eq Ξ Ξ ==> eq) s_subst.
intros ??[] -> ??? H1; cbn; now rewrite H1.
Qed.
(*
#[global] Instance a_comp_eq {Ξ1 Ξ2 Ξ3} : Proper (ass_eq _ _ ==> ass_eq _ _ ==> ass_eq _ _) (@a_comp Ξ1 Ξ2 Ξ3).
intros ? ? H1 ? ? H2 ? ?; unfold a_comp, s_map; now rewrite H1, H2.
Qed.
*)
Lemma 7: renaming-substitution "associativity".
Definition t_ren_sub_P Ξ1 (t : term Ξ1) : Prop :=
forall Ξ2 Ξ3 (f1 : Ξ1 =[val_m]> Ξ2) (f2 : Ξ2 β Ξ3) ,
(t `ββ f1) ββα΅£ f2 = t `ββ (f1 βα΅£ f2) .
Definition v_ren_sub_P Ξ1 (v : val Ξ1) : Prop :=
forall Ξ2 Ξ3 (f1 : Ξ1 =[val_m]> Ξ2) (f2 : Ξ2 β Ξ3) ,
(v `α΅₯β f1) α΅₯βα΅£ f2 = v `α΅₯β (f1 βα΅£ f2) .
Definition e_ren_sub_P Ξ1 (Ο : ev_ctx Ξ1) : Prop :=
forall Ξ2 Ξ3 (f1 : Ξ1 =[val_m]> Ξ2) (f2 : Ξ2 β Ξ3) ,
(Ο `ββ f1) ββα΅£ f2 = Ο `ββ (f1 βα΅£ f2) .
Lemma ren_sub_prf : syn_ind_args t_ren_sub_P v_ren_sub_P e_ren_sub_P.
econstructor.
all: unfold t_ren_sub_P, v_ren_sub_P, e_ren_sub_P.
all: intros; cbn; f_equal; auto.
change (a_shift1 (fun x => _)) with (a_shift1 (a:=β) (f1 βα΅£ f2)); now rewrite a_shift1_a_ren.
Qed.
Lemma t_ren_sub {Ξ1 Ξ2 Ξ3} (f1 : Ξ1 =[val_m]> Ξ2) (f2 : Ξ2 β Ξ3) (t : term Ξ1)
: (t `ββ f1) ββα΅£ f2 = t `ββ (f1 βα΅£ f2) .
now apply (term_ind_mut _ _ _ ren_sub_prf).
Qed.
Lemma v_ren_sub {Ξ1 Ξ2 Ξ3} (f1 : Ξ1 =[val_m]> Ξ2) (f2 : Ξ2 β Ξ3) (v : val Ξ1)
: (v `α΅₯β f1) α΅₯βα΅£ f2 = v `α΅₯β (f1 βα΅£ f2) .
now apply (val_ind_mut _ _ _ ren_sub_prf).
Qed.
Lemma e_ren_sub {Ξ1 Ξ2 Ξ3} (f1 : Ξ1 =[val_m]> Ξ2) (f2 : Ξ2 β Ξ3) (Ο : ev_ctx Ξ1)
: (Ο `ββ f1) ββα΅£ f2 = Ο `ββ (f1 βα΅£ f2) .
now apply (ctx_ind_mut _ _ _ ren_sub_prf).
Qed.
Lemma m_ren_sub {Ξ1 Ξ2 Ξ3} (f1 : Ξ1 =[val_m]> Ξ2) (f2 : Ξ2 β Ξ3) a (v : val_m Ξ1 a)
: (v α΅₯β f1) ββα΅£ f2 = v α΅₯β (f1 βα΅£ f2) .
destruct a; [ now apply v_ren_sub | now apply e_ren_sub ].
Qed.
Lemma s_ren_sub {Ξ1 Ξ2 Ξ3} (f1 : Ξ1 =[val_m]> Ξ2) (f2 : Ξ2 β Ξ3) (s : state Ξ1)
: (s ββ f1) ββα΅£ f2 = s ββ (f1 βα΅£ f2) .
destruct s; cbn; now rewrite t_ren_sub, e_ren_sub.
Qed.
Lemma 8: substitution-renaming "associativity".
Definition t_sub_ren_P Ξ1 (t : term Ξ1) : Prop :=
forall Ξ2 Ξ3 (f1 : Ξ1 β Ξ2) (f2 : Ξ2 =[val_m]> Ξ3) ,
(t ββα΅£ f1) `ββ f2 = t `ββ (f1 α΅£β f2) .
Definition v_sub_ren_P Ξ1 (v : val Ξ1) : Prop :=
forall Ξ2 Ξ3 (f1 : Ξ1 β Ξ2) (f2 : Ξ2 =[val_m]> Ξ3) ,
(v α΅₯βα΅£ f1) `α΅₯β f2 = v `α΅₯β (f1 α΅£β f2) .
Definition e_sub_ren_P Ξ1 (Ο : ev_ctx Ξ1) : Prop :=
forall Ξ2 Ξ3 (f1 : Ξ1 β Ξ2) (f2 : Ξ2 =[val_m]> Ξ3) ,
(Ο ββα΅£ f1) `ββ f2 = Ο `ββ (f1 α΅£β f2) .
Lemma sub_ren_prf : syn_ind_args t_sub_ren_P v_sub_ren_P e_sub_ren_P.
econstructor.
all: unfold t_sub_ren_P, v_sub_ren_P, e_sub_ren_P.
all: intros; cbn; f_equal; auto.
now rewrite a_shift1_s_ren.
Qed.
Lemma t_sub_ren {Ξ1 Ξ2 Ξ3} (f1 : Ξ1 β Ξ2) (f2 : Ξ2 =[val_m]> Ξ3) (t : term Ξ1)
: (t ββα΅£ f1) `ββ f2 = t `ββ (f1 α΅£β f2) .
now apply (term_ind_mut _ _ _ sub_ren_prf).
Qed.
Lemma v_sub_ren {Ξ1 Ξ2 Ξ3} (f1 : Ξ1 β Ξ2) (f2 : Ξ2 =[val_m]> Ξ3) (v : val Ξ1)
: (v α΅₯βα΅£ f1) `α΅₯β f2 = v `α΅₯β (f1 α΅£β f2) .
now apply (val_ind_mut _ _ _ sub_ren_prf).
Qed.
Lemma e_sub_ren {Ξ1 Ξ2 Ξ3} (f1 : Ξ1 β Ξ2) (f2 : Ξ2 =[val_m]> Ξ3) (Ο : ev_ctx Ξ1)
: (Ο ββα΅£ f1) `ββ f2 = Ο `ββ (f1 α΅£β f2) .
now apply (ctx_ind_mut _ _ _ sub_ren_prf).
Qed.
Lemma m_sub_ren {Ξ1 Ξ2 Ξ3} (f1 : Ξ1 β Ξ2) (f2 : Ξ2 =[val_m]> Ξ3) a (v : val_m Ξ1 a)
: (v ββα΅£ f1) α΅₯β f2 = v α΅₯β (f1 α΅£β f2) .
destruct a; [ now apply v_sub_ren | now apply e_sub_ren ].
Qed.
Lemma s_sub_ren {Ξ1 Ξ2 Ξ3} (f1 : Ξ1 β Ξ2) (f2 : Ξ2 =[val_m]> Ξ3) (s : state Ξ1)
: (s ββα΅£ f1) ββ f2 = s ββ (f1 α΅£β f2) .
destruct s; cbn; now rewrite t_sub_ren, e_sub_ren.
Qed.
Lemma 9: left identity law of substitution.
Definition t_sub_id_l_P Ξ (t : term Ξ) : Prop := t `ββ a_id = t .
Definition v_sub_id_l_P Ξ (v : val Ξ) : Prop := v `α΅₯β a_id = v .
Definition e_sub_id_l_P Ξ (Ο : ev_ctx Ξ) : Prop := Ο `ββ a_id = Ο .
Lemma sub_id_l_prf : syn_ind_args t_sub_id_l_P v_sub_id_l_P e_sub_id_l_P.
econstructor.
all: unfold t_sub_id_l_P, v_sub_id_l_P, e_sub_id_l_P.
all: intros; cbn; f_equal; auto.
now rewrite a_shift_id.
Qed.
Lemma t_sub_id_l {Ξ} (t : term Ξ) : t `ββ a_id = t .
now apply (term_ind_mut _ _ _ sub_id_l_prf).
Qed.
Lemma v_sub_id_l {Ξ} (v : val Ξ) : v `α΅₯β a_id = v .
now apply (val_ind_mut _ _ _ sub_id_l_prf).
Qed.
Lemma e_sub_id_l {Ξ} (Ο : ev_ctx Ξ) : Ο `ββ a_id = Ο .
now apply (ctx_ind_mut _ _ _ sub_id_l_prf).
Qed.
Lemma m_sub_id_l {Ξ} a (v : val_m Ξ a) : v α΅₯β a_id = v .
destruct a; [ now apply v_sub_id_l | now apply e_sub_id_l ].
Qed.
Lemma s_sub_id_l {Ξ} (s : state Ξ) : s ββ a_id = s .
destruct s; cbn; now rewrite t_sub_id_l, e_sub_id_l.
Qed.
Lemma 9: right identity law of substitution. As for renaming, this one is mostly by definition.
Lemma m_sub_id_r {Ξ1 Ξ2} {a} (i : Ξ1 β a) (f : Ξ1 =[val_m]> Ξ2) : a_id a i α΅₯β f = f a i.
now destruct a.
Qed.
Lemma 10: shifting assigments respects composition.
Lemma a_shift1_comp {Ξ1 Ξ2 Ξ3 a} (f1 : Ξ1 =[val_m]> Ξ2) (f2 : Ξ2 =[val_m]> Ξ3)
: @a_shift1 _ _ a (f1 β f2) β‘β a_shift1 f1 β a_shift1 f2 .
intros ? v; dependent elimination v; cbn.
now rewrite m_sub_id_r.
unfold m_shift1; now rewrite m_ren_sub, m_sub_ren.
Qed.
Lemma 11: substitution-substitution associativity, ie composition law.
Definition t_sub_sub_P Ξ1 (t : term Ξ1) : Prop :=
forall Ξ2 Ξ3 (f1 : Ξ1 =[val_m]> Ξ2) (f2 : Ξ2 =[val_m]> Ξ3) ,
t `ββ (f1 β f2) = (t `ββ f1) `ββ f2 .
Definition v_sub_sub_P Ξ1 (v : val Ξ1) : Prop :=
forall Ξ2 Ξ3 (f1 : Ξ1 =[val_m]> Ξ2) (f2 : Ξ2 =[val_m]> Ξ3) ,
v `α΅₯β (f1 β f2) = (v `α΅₯β f1) `α΅₯β f2 .
Definition e_sub_sub_P Ξ1 (Ο : ev_ctx Ξ1) : Prop :=
forall Ξ2 Ξ3 (f1 : Ξ1 =[val_m]> Ξ2) (f2 : Ξ2 =[val_m]> Ξ3) ,
Ο `ββ (f1 β f2) = (Ο `ββ f1) `ββ f2 .
Lemma sub_sub_prf : syn_ind_args t_sub_sub_P v_sub_sub_P e_sub_sub_P.
econstructor.
all: unfold t_sub_sub_P, v_sub_sub_P, e_sub_sub_P.
all: intros; cbn; f_equal; auto.
now rewrite a_shift1_comp.
Qed.
Lemma t_sub_sub {Ξ1 Ξ2 Ξ3} (f1 : Ξ1 =[val_m]> Ξ2) (f2 : Ξ2 =[val_m]> Ξ3) (t : term Ξ1)
: t `ββ (f1 β f2) = (t `ββ f1) `ββ f2 .
now apply (term_ind_mut _ _ _ sub_sub_prf).
Qed.
Lemma v_sub_sub {Ξ1 Ξ2 Ξ3} (f1 : Ξ1 =[val_m]> Ξ2) (f2 : Ξ2 =[val_m]> Ξ3) (v : val Ξ1)
: v `α΅₯β (f1 β f2) = (v `α΅₯β f1) `α΅₯β f2 .
now apply (val_ind_mut _ _ _ sub_sub_prf).
Qed.
Lemma e_sub_sub {Ξ1 Ξ2 Ξ3} (f1 : Ξ1 =[val_m]> Ξ2) (f2 : Ξ2 =[val_m]> Ξ3) (Ο : ev_ctx Ξ1)
: Ο `ββ (f1 β f2) = (Ο `ββ f1) `ββ f2 .
now apply (ctx_ind_mut _ _ _ sub_sub_prf).
Qed.
Lemma m_sub_sub {Ξ1 Ξ2 Ξ3} a (v : val_m Ξ1 a) (f1 : Ξ1 =[val_m]> Ξ2) (f2 : Ξ2 =[val_m]> Ξ3)
: v α΅₯β (f1 β f2) = (v α΅₯β f1) α΅₯β f2 .
destruct a; [ now apply v_sub_sub | now apply e_sub_sub ].
Qed.
Lemma s_sub_sub {Ξ1 Ξ2 Ξ3} (s : state Ξ1) (f1 : Ξ1 =[val_m]> Ξ2) (f2 : Ξ2 =[val_m]> Ξ3)
: s ββ (f1 β f2) = (s ββ f1) ββ f2 .
destruct s; cbn; now rewrite t_sub_sub, e_sub_sub.
Qed.
Lemma a_sub1_sub {Ξ Ξ a} (f : Ξ =[val_m]> Ξ) (v : val_m Ξ a)
: a_shift1 f β assign1 (v α΅₯β f) β‘β (assign1 v) β f .
intros ? i; dependent elimination i; cbn.
now rewrite m_sub_id_r.
unfold m_shift1; rewrite m_sub_ren, m_sub_id_r; now apply m_sub_id_l.
Qed.
Lemma t_sub1_sub {Ξ Ξ x} (f : Ξ =[val_m]> Ξ) (t : term (Ξ βΆβ x)) v
: (t `ββ a_shift1 f) /[ v α΅₯β f ] = (t /[ v ]) `ββ f.
unfold t_subst1; now rewrite <- 2 t_sub_sub, a_sub1_sub.
Qed.
We can now instanciate the generic OGS construction.
#[global] Instance ulc_val_laws : subst_monoid_laws val_m := {| v_sub_proper := @m_sub_eq ; v_sub_var := @m_sub_id_r ; v_var_sub := @m_sub_id_l ; Subst.v_sub_sub := @m_sub_sub |} . #[global] Instance ulc_conf_laws : subst_module_laws val_m state := {| c_sub_proper := @s_sub_eq ; c_var_sub := @s_sub_id_l ; c_sub_sub := @s_sub_sub |} . #[global] Instance ulc_var_laws : var_assumptions val_m. econstructor. - intros ? [] ?? H; now dependent destruction H. - intros ? [] []; try now apply Yes; exact (Vvar _). all: apply No; intro H; dependent destruction H. - intros ?? [] v r H; induction v; dependent destruction H; exact (Vvar _). Qed. #[global] Instance ulc_machine : machine val_m state obs_op := {| eval := @ulc_eval ; oapp := @obs_app |} . From Coinduction Require Import coinduction lattice rel tactics. From OGS.ITree Require Import Eq. Ltac refold_eval := change (Structure.iter _ _ ?a) with (ulc_eval a); change (Structure.subst (fun pat : T1 => let 'T1_0 := pat in ?f) T1_0 ?u) with (bind_delay' u f). #[global] Instance ulc_machine_law : machine_laws val_m state obs_op. econstructor; cbn; intros. - intros ?? H1; dependent elimination o; cbn; repeat (f_equal; auto). - destruct x; dependent elimination o; cbn; f_equal. - revert c a; unfold comp_eq, it_eq; coinduction R CIH; intros c e. cbn; funelim (eval_step c); cbn. + destruct (e (β) i); auto. remember (v `α΅₯β e) as v'; clear H v Heqv'. dependent elimination v'; cbn; auto. + econstructor; refold_eval; apply CIH. + remember (e (β) i) as vv; clear H i Heqvv. dependent elimination vv; cbn; auto. + econstructor; refold_eval; change (?v `α΅₯β ?a) with ((v : val_m _ (β)) α΅₯β a). rewrite t_sub1_sub. apply CIH. + econstructor; refold_eval; apply CIH. - destruct u as [ a i [ p Ξ³ ] ]; cbn. dependent elimination p; cbn. all: unfold comp_eq; apply it_eq_unstep; cbn; econstructor. all: econstructor; intros ? v; do 3 (dependent elimination v; auto). - intros [ x p ]. destruct x; dependent elimination p; econstructor. * intros [ z p ] H. dependent elimination p; dependent elimination H. all: dependent elimination v; try now destruct (t0 (Vvar _)). all: apply it_eq_step in i0; now inversion i0. * intros [ z p ] H. dependent elimination p; dependent elimination H; cbn in *. dependent elimination v; try now destruct (t0 (Vvar _)). + apply it_eq_step in i0; now inversion i0. + remember (a _ Ctx.top) as vv; clear a Heqvv. dependent elimination vv; apply it_eq_step in i0; cbn in i0; dependent elimination i0. inversion r_rel. + econstructor; intros [ z p ] H. dependent elimination p; dependent elimination H. all: dependent elimination v0; try now destruct (t1 (Vvar _)). all: apply it_eq_step in i2; now inversion i2. Qed.
Definition subst_eq Ξ {Ξ} : relation (state Ξ) :=
fun u v => forall Ο : Ξ =[val_m]> Ξ, evalβ (u ββ Ο) β evalβ (v ββ Ο) .
Notation "x ββ¦sub Ξ β§β y" := (subst_eq Ξ x y) (at level 10).
Theorem ulc_subst_correct Ξ {Ξ} (x y : state Ξ)
: x ββ¦ogs Ξβ§β y -> x ββ¦sub Ξβ§β y .
exact (ogs_correction Ξ x y).
Qed.
Definition ciu_eq Ξ {Ξ} : relation (term Ξ) :=
fun u v => forall (Ο : Ξ =[val_m]> Ξ) (Ο : ev_ctx Ξ),
evalβ (Cut (u `ββ Ο) Ο) β evalβ (Cut (v `ββ Ο) Ο) .
Notation "x ββ¦ciu Ξ β§β y" := (ciu_eq Ξ x y) (at level 10).
Definition c_init {Ξ} (t : term Ξ) : state (Ξ βΆβ β)
:= Cut (t_shift1 t) (K0 Ctx.top) .
Definition a_of_sk {Ξ Ξ} (Ο : Ξ =[val_m]> Ξ) (Ο : ev_ctx Ξ)
: (Ξ βΆβ β) =[val_m]> Ξ := Ο βΆβ (Ο : val_m _ (β)) .
Lemma sub_init {Ξ Ξ} (t : term Ξ) (Ο : Ξ =[val_m]> Ξ) (Ο : ev_ctx Ξ)
: Cut (t `ββ Ο) Ο = c_init t ββ a_of_sk Ο Ο .
cbn; unfold t_shift1; now rewrite t_sub_ren.
Qed.
We can now obtain a correctness theorem with respect to standard CIU-equivalence by embedding terms into states. Proving that CIU-equivalence entails our substitution equivalence is left to the reader!
Theorem ulc_ciu_correct Ξ {Ξ} (x y : term Ξ)
: c_init x ββ¦ogs Ξβ§β c_init y -> x ββ¦ciu Ξβ§β y .
intros H Ο k; rewrite 2 sub_init.
now apply ulc_subst_correct.
Qed.
Following a trick by Conor McBride we provide a cool notation for writing terms without any DeBruijn indices, instead leveraging Coq's binders. For this we need a bit of infrastructure, for manipulating terms that only have term variables.
Equations n_ctx : nat -> t_ctx :=
n_ctx O := β
;
n_ctx (S n) := n_ctx n βΆβ β .
Notation var' n := (n_ctx n β β).
Notation term' n := (term (n_ctx n)).
Equations v_emb (n : nat) : var' (S n) :=
v_emb O := Ctx.top ;
v_emb (S n) := pop (v_emb n) .
Equations v_lift {n} : var' n -> var' (S n) :=
@v_lift (S _) Ctx.top := Ctx.top ;
@v_lift (S _) (pop i) := pop (v_lift i) .
Here starts the magic.
Equations mk_var (m : nat) : forall n, var' (m + S n) := mk_var O := v_emb ; mk_var (S m) := v_lift β mk_var m . Definition mk_lam {m : nat} (f : (forall n, term' (m + S n)) -> term' (S m)) : term' m := Val (Lam (f (Val β Var β mk_var m))). Arguments mk_lam {_} & _ .
Now a bit of syntactic sugar.
Declare Custom Entry lambda.
Notation "β¨ e β¨" := (e : term' 0) (e custom lambda at level 2).
Notation "x" := (x _) (in custom lambda at level 0, x ident).
Notation "( x )" := x (in custom lambda at level 0, x at level 2).
Notation "x y" := (App x y)
(in custom lambda at level 1, left associativity).
Notation "'Ξ»' x .. y β U" :=
(mk_lam (fun x => .. (mk_lam (fun y => U)) ..))
(in custom lambda at level 1, x binder, y binder, U at level 2).
Aren't these beautiful?
Definition Delta := β¨ (Ξ» x β x x) β¨ .
Definition Omega := β¨ (Ξ» x β x x) (Ξ» x β x x) β¨ .
Definition Upsilon := β¨ Ξ» f β (Ξ» x β f (x x)) (Ξ» x β f (x x)) β¨.
Definition Theta := β¨ (Ξ» x f β f (x x f)) (Ξ» x f β f (x x f)) β¨.
You can check the actual lambda-term they unwrap to by running eg:
Eval cbv in Upsilon.