Built with Alectryon, running Coq+SerAPI v8.17.0+0.17.1. Bubbles () indicate interactive fragments: hover for details, tap to reveal contents. Use Ctrl+↑ Ctrl+↓ to navigate, Ctrl+πŸ–±οΈ to focus. On Mac, use ⌘ instead of Ctrl.

A Minimal Example: Call-by-Value Simply Typed Lambda Calculus

We demonstrate how to instantiate our framework to define the OGS associated to the CBV Ξ»-calculus. With the instance comes the proof that bisimilarity of the OGS entails substitution equivalence, which coincides with CIU-equivalence.

Note

This example is designed to be minimal, hiding by nature some difficulties. In particular it has no positive types, which simplifies the development a lot.

Syntax

Types

As discussed in the paper, our framework applies not really to a language but more to an abstract machine. In order to ease this instanciation, we will focus directly on a CBV machine and define evaluation contexts early on. Working with intrinsically typed syntax, we need to give types to these contexts: we will type them by the "formal negation" of the type of their hole. In order to do so we first define the usual types ty0 of STLC with functions and a ground type.

Declare Scope ty_scope .
Inductive ty0 : Type :=
| ΞΉ : ty0
| Arr : ty0 -> ty0 -> ty0
.
Notation "A β†’ B" := (Arr A B) (at level 40) : ty_scope .

We then define "tagged types", where t+ a will be the type of terms of type a, and t- a the type of evaluation contexts of hole a.

Variant ty : Type :=
| VTy : ty0 -> ty
| KTy : ty0 -> ty
.
Notation "+ t" := (VTy t) (at level 5) : ty_scope .
Notation "Β¬ t" := (KTy t) (at level 5) : ty_scope .
Coercion VTy : ty0 >-> ty .

Typing Contexts

Typing contexts are now simply defined as lists of tagged types. This is perhaps the slightly surprising part: contexts will now contain "continuation variables". Rest assured terms will make no use of them. These are solely needed for evaluation contexts: as they are only typed with their hole, we are missing the type of their outside. We fix this problem by naming the outside and make evaluation contexts end with a continuation variable.

Note

Our choices make a bit more sense if we realize that what we are writing is exactly the subset of λμ-calculus that is the image of the CBV translation from λ-calculus.

Definition t_ctx : Type := ctx ty .

Terms and Values and ...

We now have all that is needed to define terms, which we define mutually with values. As discussed above, they are are indexed by a list of tagged types (of which they only care about the non-negated elements) and by an untagged type.

The only fanciness is the recursive lambda abstraction, which we include to safeguard ourselves from accidentally using the fact that the language is strongly normalizing.

Inductive term (Ξ“ : t_ctx) : ty0 -> Type :=
| Val {a} : val Ξ“ a -> term Ξ“ a
| App {a b} : term Ξ“ (a β†’ b) -> term Ξ“ a -> term Ξ“ b
with val (Ξ“ : t_ctx) : ty0 -> Type :=
| Var {a} : Ξ“ βˆ‹ + a -> val Ξ“ a
| TT : val Ξ“ ΞΉ
| Lam {a b} : term (Ξ“ β–Άβ‚“ (a β†’ b) β–Άβ‚“ a) b -> val Ξ“ (a β†’ b)
.

Evaluation contexts follow. As discussed, there is a "covariable" case, to "end" the context. The other cases are usual: evaluating the argument of an application and evaluating the function.

Inductive ev_ctx (Ξ“ : t_ctx) : ty0 -> Type :=
| K0 {a} : Ξ“ βˆ‹ Β¬ a -> ev_ctx Ξ“ a
| K1 {a b} : term Ξ“ (a β†’ b) -> ev_ctx Ξ“ b -> ev_ctx Ξ“ a
| K2 {a b} : val Ξ“ a -> ev_ctx Ξ“ b -> ev_ctx Ξ“ (a β†’ b)
.

Next are the machine states. They consist of an explicit cut and represent a term in the process of being executed in some context. Notice how states they are only indexed by a typing context: these are proper "diverging programs".

Definition state := term βˆ₯β‚› ev_ctx.

Finally the last of syntactic categories: "machine values". This category is typed with a tagged type and encompasses both values (for non-negated types) and evaluation contexts (for negated types). The primary use-case for this category is to denote "things by which we can substitute (tagged) variables".

In fact when working with intrinsically-typed syntax, substitution is modelled as a monoidal multiplication (see [AACMM21] and [FS22]). We will prove later that val_m Ξ“ is indeed a monoid relative to has Ξ“ and that contexts and assignments form a category.

Equations val_m : t_ctx -> ty -> Type :=
  val_m Ξ“ (+ a) := val Ξ“ a ;
  val_m Ξ“ (Β¬ a) := ev_ctx Ξ“ a .

Together with machine values we define a smart constructor for "machine var", embedding tagged variables into these generalized values. It conveniently serves as the identity assignment, a fact we use to give it this mysterious point-free style type, which desugars to forall a, Ξ“ βˆ‹ a -> val_m Ξ“ a.

Equations a_id {Ξ“} : Ξ“ =[val_m]> Ξ“ :=
  a_id (+ _) i := Var i ;
  a_id (Β¬ _) i := K0 i .

Substitution and Renaming

In order to define substitution we first need to dance the intrinsically-typed dance and define renamings, from which we will derive weakenings and then only define substitution.

Renaming

Lets write intrinsically-typed parallel renaming for all of our syntactic categories! If you have never seen such intrinsically-typed definitions you might be surprised by the absence of error-prone de-bruijn index manipulation. Enjoy this beautiful syntax traversal!

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 _ (TT)       := TT ;
  v_rename f _ (Lam t) := Lam (t_rename (r_shift2 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 _ Ο€ .

#[global] Arguments s_rename _ _ _ /.
#[global] Arguments m_rename _ _ _ /.

We can recast m_rename as an operator on assigments, more precisely as renaming an assigment on the left.

Definition a_ren {Ξ“1 Ξ“2 Ξ“3} : Ξ“2 βŠ† Ξ“3 -> Ξ“1 =[val_m]> Ξ“2 -> Ξ“1 =[val_m]> Ξ“3 :=
  fun f g _ i => m_rename f _ (g _ i) .

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 r%asgn a) (at level 14) : asgn_scope.

As discussed above, we can now obtain our precious weakenings. Here are the three we will need.

Definition t_shift {Ξ“ a} := @t_rename Ξ“ (Ξ“ β–Άβ‚“ a) r_pop.
Definition m_shift2 {Ξ“ a b} := @m_rename Ξ“ (Ξ“ β–Άβ‚“ a β–Άβ‚“ b) (r_pop α΅£βŠ› r_pop).
Definition a_shift2 {Ξ“ Ξ” a b} (f : Ξ“ =[val_m]> Ξ”)
  : (Ξ“ β–Άβ‚“ a β–Άβ‚“ b) =[val_m]> (Ξ” β–Άβ‚“ a β–Άβ‚“ b)
  := [ [ a_map f m_shift2 ,β‚“ a_id a (pop top) ] ,β‚“ a_id b top ].

Substitutions

With weakenings in place we are now equipped to define substitutions. This goes pretty much like renaming. We have abstained from defining generic syntax traversal tools like Allais et al's "Kits" to keep our example minimal... And incidentally showcase the intrinsically-typed style with Matthieu Sozeau's Equations.

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 _ (TT)       := TT ;
  v_subst f _ (Lam t) := Lam (t_subst (a_shift2 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 _ Ο€) .

These notations will make everything shine.

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).

These two last substitutions, for states and generalized values, are the ones we will give to the abstract interface. For categorical abstraction reasons, the abstract interface has the argument reversed for substitutions: first the state or value, then the assignment as second argument. To ease instanciation we will hence do so too. The type is given with tricky combinators but expands to:

m_subst Ξ“ a : val_m Ξ“ a -> forall Ξ”, Ξ“ =[val_m]> Ξ” -> val_m Ξ” a

Equations m_subst : val_m ⇒₁ ⟦ val_m , val_m βŸ§β‚ :=
  m_subst _ (+ _) v _ f := v `α΅₯βŠ› f ;
  m_subst _ (Β¬ _) Ο€ _ f := Ο€ `β‚‘βŠ› f .

Definition s_subst : state β‡’β‚€ ⟦ val_m , state βŸ§β‚€ :=
  fun _ s _ f => (s.(cut_l) `β‚œβŠ› f) β‹… (s.(cut_r) `β‚‘βŠ› f).
#[global] Arguments s_subst _ _ /.

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 |} .

Finally we define a more usual substitution function which only substitutes the top two variables instead of doing parallel substitution.

Definition assign2 {Ξ“ a b} v1 v2
  : (Ξ“ β–Άβ‚“ a β–Άβ‚“ b) =[val_m]> Ξ“
  := [ [ a_id ,β‚“ v1 ] ,β‚“ v2 ] .
Definition t_subst2 {Ξ“ a b c} (t : term _ c) v1 v2 := t `β‚œβŠ› @assign2 Ξ“ a b v1 v2.
Notation "t /[ v1 , v2 ]" := (t_subst2 t v1 v2) (at level 50, left associativity).

An Evaluator

As motivated earlier, the evaluator will be a defined as a state-machine, the core definition being a state-transition function. To stick to the intrinsic style, we wish that this state-machine stops only at evidently normal forms, instead of stoping at states which happen to be in normal form. Perhaps more simply said, we want to type the transition function as state Ξ“ β†’ (state Ξ“ + nf Ξ“), where returning in the right component means stoping and outputing a normal form.

In order to do this we first need to define normal forms! But instead of defining an inductive definition of normal forms as is customary, we will take an other route more tailored to OGS based on ultimate patterns.

Patterns and Observations

As discussed in the paper, a central aspect of OGS is to circumvent the problem of naive trace semantics for higher-order languages by mean of a notion of "abstract values", or more commonly ultimate patterns, which define the "shareable part" of a value. For clarity reasons, instead of patterns we here take the dual point of view of "observations", which can be seen as patterns at the dual type (dual in the sense of swapping the tag). For our basic Ξ»-calculus, all types are negative -- that is, unsheareble -- hence the observations are pretty simple:

  • Observing a continuation of type Β¬ a means returning a (hidden) value to it. In terms of pattern this corresponds to the "fresh variable" pattern Var x.

  • Observing a function of type a β†’ b means giving it a hidden value and a hidden continuation. In terms of patterns this corresponds to the "application" co-pattern K2 (Var x) (K0 y).

    Variant obs : ty -> Type :=
    | ORet {a} : obs (Β¬ a)
    | OApp {a b} : obs (a β†’ b)
    .

As observations correspond to a subset of (machine) values where all the variables are "fresh", these variables have no counter-part in de-bruijn notation (there is no meaningful notion of freshness). As such we have not indexed obs by any typing context, but to complete the definition we now need to define a projection into typing contexts denoting the "domain", "support" or more accurately "set of nameless fresh variables" of an observation.

Equations obs_dom {a} : obs a -> t_ctx :=
  obs_dom (@ORet a)   := βˆ… β–Άβ‚“ + a ;
  obs_dom (@OApp a b) := βˆ… β–Άβ‚“ + a β–Άβ‚“ Β¬ b .

These two definitions together form an operator.

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 _).

Given a value, an observation on its type and a value for each fresh variable of the observation, we can "refold" everything and form a new state which will indeed observe this value.

Equations obs_app {Ξ“ a} (v : val_m Ξ“ a) (p : obs a) (Ξ³ : obs_dom p =[val_m]> Ξ“) : state Ξ“ :=
  obs_app v OApp Ξ³ := Val v β‹… K2 (Ξ³ _ (pop top)) (Ξ³ _ top) ;
  obs_app Ο€ ORet Ξ³ := Val (Ξ³ _ top) β‹… (Ο€ : ev_ctx _ _) .

Normal forms

Normal forms for CBV Ξ»-calculus can be characterized as either a value v or a stuck application in evaluation context E[x v] (see "eager-normal forms" in [L05]). Now it doesn't take much squinting to see that in our setting, this corresponds respectively to states of the form ⟨ v | K0 x ⟩ and ⟨ Var x | K2 v Ο€ ⟩. Squinting a bit more, we can reap the benefits of our unified treatment of terms and contexts and see that both of these cases work in the same way: normal states are states given by a variable facing an observation whose fresh variables have been substituted with values.

Having already defined observation and their set of fresh variables, an observation stuffed with values in typing context Ξ“ can be represented simply by a formal substitution of an observation o and an assigment obs_dom o =[val]> Ξ“. This "split" definition of normal forms is the one we will take.

Definition nf := c_var βˆ₯β‚› (obs_op # val_m).

The CBV Machine

Everything is now in place to define our state transition function. The reduction rules should come to no surprise:

  1. ⟨ t1 t2 | Ο€ ⟩ β†’ ⟨ t2 | t1 β‹…1 Ο€ ⟩
  2. ⟨ v | x ⟩ normal
  3. ⟨ v | t β‹…1 Ο€ ⟩ β†’ ⟨ t | v β‹…2 Ο€ ⟩
  4. ⟨ x | v β‹…2 Ο€ ⟩ normal
  5. ⟨ Ξ»fx.t | v β‹…2 Ο€ ⟩ β†’ ⟨ t[f↦λfx.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 Ξ“ -> sum (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 /[ Lam t , v ]) Ο€) .

Having defined the transition function, we can now iterate it inside the delay monad. This constructs a possibly non-terminating computation ending with a normal form.

Definition stlc_eval {Ξ“ : t_ctx} : state Ξ“ -> delay (nf Ξ“)
  := iter_delay (ret_delay ∘ eval_step).

Properties

We have now finished all the computational parts of the instanciation, but all the proofs are left to be done. Before attacking the OGS-specific hypotheses, we will need to prove the usual standard lemmata on renaming and substitution.

There will be a stack of lemmata which will all pretty much be simple inductions on the syntax, so we start by introducing some helpers for this. In fact it is not completely direct to do since terms and values are mutually defined: we will need to derive a mutual induction principle.

Scheme term_mut := Induction for term Sort Prop
   with val_mut := Induction for val Sort Prop .

Annoyingly, Coq treats this mutual induction principle as two separate induction principles. They both have the exact same premises but differ in their conclusion. Thus we define a datatype for these premises, to avoid duplicating the proofs. Additionally, evaluation contexts are not defined mutually with terms and values, but it doesn't hurt to prove their properties simultaneously too, so syn_ind_args is in fact closer to the premises of a three-way mutual induction principle between terms, values and evaluation contexts.

Record syn_ind_args (P_t : forall Ξ“ A, term Ξ“ A -> Prop)
                    (P_v : forall Ξ“ A, val Ξ“ A -> Prop)
                    (P_e : forall Ξ“ A, ev_ctx Ξ“ A -> Prop) :=
  {
    ind_val {Ξ“ a} v (_ : P_v Ξ“ a v) : P_t Ξ“ a (Val v) ;
    ind_app {Ξ“ a b} t1 (_ : P_t Ξ“ (a β†’ b) t1) t2 (_ : P_t Ξ“ a t2) : P_t Ξ“ b (App t1 t2) ;
    ind_var {Ξ“ a} i : P_v Ξ“ a (Var i) ;
    ind_tt {Ξ“} : P_v Ξ“ (ΞΉ) (TT) ;
    ind_lamrec {Ξ“ a b} t (_ : P_t _ b t) : P_v Ξ“ (a β†’ b) (Lam t) ;
    ind_kvar {Ξ“ a} i : P_e Ξ“ a (K0 i) ;
    ind_kfun {Ξ“ a b} t (_ : P_t Ξ“ (a β†’ b) t) Ο€ (_ : P_e Ξ“ b Ο€) : P_e Ξ“ a (K1 t Ο€) ;
    ind_karg {Ξ“ a b} v (_ : P_v Ξ“ a v) Ο€ (_ : P_e Ξ“ b Ο€) : P_e Ξ“ (a β†’ b) (K2 v Ο€)
  } .

P_t: forall (Ξ“ : t_ctx) (A : ty0), term Ξ“ A -> Prop
P_v: forall (Ξ“ : t_ctx) (A : ty0), val Ξ“ A -> Prop
P_e: forall (Ξ“ : t_ctx) (A : ty0), ev_ctx Ξ“ A -> Prop
H: syn_ind_args P_t P_v P_e
Ξ“: t_ctx
a: ty0
t: term Ξ“ a

P_t Ξ“ a t
destruct H; now apply (term_mut P_t P_v). Qed.
P_t: forall (Ξ“ : t_ctx) (A : ty0), term Ξ“ A -> Prop
P_v: forall (Ξ“ : t_ctx) (A : ty0), val Ξ“ A -> Prop
P_e: forall (Ξ“ : t_ctx) (A : ty0), ev_ctx Ξ“ A -> Prop
H: syn_ind_args P_t P_v P_e
Ξ“: t_ctx
a: ty0
v: val Ξ“ a

P_v Ξ“ a v
destruct H; now apply (val_mut P_t P_v). Qed.
P_t: forall (Ξ“ : t_ctx) (A : ty0), term Ξ“ A -> Prop
P_v: forall (Ξ“ : t_ctx) (A : ty0), val Ξ“ A -> Prop
P_e: forall (Ξ“ : t_ctx) (A : ty0), ev_ctx Ξ“ A -> Prop
H: syn_ind_args P_t P_v P_e
Ξ“: t_ctx
a: ty0
Ο€: ev_ctx Ξ“ a

P_e Ξ“ a Ο€
P_t: forall (Ξ“ : t_ctx) (A : ty0), term Ξ“ A -> Prop
P_v: forall (Ξ“ : t_ctx) (A : ty0), val Ξ“ A -> Prop
P_e: forall (Ξ“ : t_ctx) (A : ty0), ev_ctx Ξ“ A -> Prop
H: syn_ind_args P_t P_v P_e
Ξ“: t_ctx
a: ty0
c: Ξ“ βˆ‹ Β¬ a

P_e Ξ“ a (K0 c)
P_t: forall (Ξ“ : t_ctx) (A : ty0), term Ξ“ A -> Prop
P_v: forall (Ξ“ : t_ctx) (A : ty0), val Ξ“ A -> Prop
P_e: forall (Ξ“ : t_ctx) (A : ty0), ev_ctx Ξ“ A -> Prop
H: syn_ind_args P_t P_v P_e
Ξ“: t_ctx
a, b: ty0
t: term Ξ“ (a β†’ b)
Ο€: ev_ctx Ξ“ b
IHΟ€: P_e Ξ“ b Ο€
P_e Ξ“ a (K1 t Ο€)
P_t: forall (Ξ“ : t_ctx) (A : ty0), term Ξ“ A -> Prop
P_v: forall (Ξ“ : t_ctx) (A : ty0), val Ξ“ A -> Prop
P_e: forall (Ξ“ : t_ctx) (A : ty0), ev_ctx Ξ“ A -> Prop
H: syn_ind_args P_t P_v P_e
Ξ“: t_ctx
a, b: ty0
v: val Ξ“ a
Ο€: ev_ctx Ξ“ b
IHΟ€: P_e Ξ“ b Ο€
P_e Ξ“ (a β†’ b) (K2 v Ο€)
P_t: forall (Ξ“ : t_ctx) (A : ty0), term Ξ“ A -> Prop
P_v: forall (Ξ“ : t_ctx) (A : ty0), val Ξ“ A -> Prop
P_e: forall (Ξ“ : t_ctx) (A : ty0), ev_ctx Ξ“ A -> Prop
H: syn_ind_args P_t P_v P_e
Ξ“: t_ctx
a: ty0
c: Ξ“ βˆ‹ Β¬ a

P_e Ξ“ a (K0 c)
apply (ind_kvar _ _ _ H).
P_t: forall (Ξ“ : t_ctx) (A : ty0), term Ξ“ A -> Prop
P_v: forall (Ξ“ : t_ctx) (A : ty0), val Ξ“ A -> Prop
P_e: forall (Ξ“ : t_ctx) (A : ty0), ev_ctx Ξ“ A -> Prop
H: syn_ind_args P_t P_v P_e
Ξ“: t_ctx
a, b: ty0
t: term Ξ“ (a β†’ b)
Ο€: ev_ctx Ξ“ b
IHΟ€: P_e Ξ“ b Ο€

P_e Ξ“ a (K1 t Ο€)
apply (ind_kfun _ _ _ H); auto; apply (term_ind_mut _ _ _ H).
P_t: forall (Ξ“ : t_ctx) (A : ty0), term Ξ“ A -> Prop
P_v: forall (Ξ“ : t_ctx) (A : ty0), val Ξ“ A -> Prop
P_e: forall (Ξ“ : t_ctx) (A : ty0), ev_ctx Ξ“ A -> Prop
H: syn_ind_args P_t P_v P_e
Ξ“: t_ctx
a, b: ty0
v: val Ξ“ a
Ο€: ev_ctx Ξ“ b
IHΟ€: P_e Ξ“ b Ο€

P_e Ξ“ (a β†’ b) (K2 v Ο€)
apply (ind_karg _ _ _ H); auto; apply (val_ind_mut _ _ _ H). Qed.

Now equipped we can start with the first lemma: renaming respects pointwise equality of assignments. As discussed, we will prove this by mutual induction on our three "base" syntactic categories of terms, values and evaluation contexts, and then we will also deduce it for the three "derived" notions of machine values, states and assigments. Sometimes some of the derived notions will be omitted if it is not needed later on.

This proof, like all the following ones will follow a simple pattern: a simplification; an application of congruence; a fixup for the two-time shifted assigment in the case of Ξ»; finally a call to the induction hypothesis.

Note

Here is definitely where the generic syntax traversal kit of Guillaume Allais et al would shine. Indeed the proof pattern i outlined can really be formalized into a generic proof.

Definition t_ren_proper_P Ξ“ a (t : term Ξ“ a) : Prop :=
  forall Ξ” (f1 f2 : Ξ“ βŠ† Ξ”), f1 ≑ₐ f2 -> t β‚œβŠ›α΅£ f1 = t β‚œβŠ›α΅£ f2 .
Definition v_ren_proper_P Ξ“ a (v : val Ξ“ a) : Prop :=
  forall Ξ” (f1 f2 : Ξ“ βŠ† Ξ”), f1 ≑ₐ f2 -> v α΅₯βŠ›α΅£ f1 = v α΅₯βŠ›α΅£ f2 .
Definition e_ren_proper_P Ξ“ a (Ο€ : ev_ctx Ξ“ a) : Prop :=
  forall Ξ” (f1 f2 : Ξ“ βŠ† Ξ”), f1 ≑ₐ f2 -> Ο€ β‚‘βŠ›α΅£ f1 = Ο€ β‚‘βŠ›α΅£ f2 .

syn_ind_args t_ren_proper_P v_ren_proper_P e_ren_proper_P

forall (Ξ“ : t_ctx) (a : ty0) (v : val Ξ“ a), v_ren_proper_P Ξ“ a v -> t_ren_proper_P Ξ“ a v

forall (Ξ“ : t_ctx) (a b : ty0) (t1 : term Ξ“ (a β†’ b)), t_ren_proper_P Ξ“ (a β†’ b) t1 -> forall t2 : term Ξ“ a, t_ren_proper_P Ξ“ a t2 -> t_ren_proper_P Ξ“ b (App t1 t2)

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ + a), v_ren_proper_P Ξ“ a (Var i)

forall Ξ“ : t_ctx, v_ren_proper_P Ξ“ ΞΉ TT

forall (Ξ“ : t_ctx) (a b : ty0) (t : term (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b), t_ren_proper_P (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b t -> v_ren_proper_P Ξ“ (a β†’ b) (Lam t)

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ Β¬ a), e_ren_proper_P Ξ“ a (K0 i)

forall (Ξ“ : t_ctx) (a b : ty0) (t : term Ξ“ (a β†’ b)), t_ren_proper_P Ξ“ (a β†’ b) t -> forall Ο€ : ev_ctx Ξ“ b, e_ren_proper_P Ξ“ b Ο€ -> e_ren_proper_P Ξ“ a (K1 t Ο€)

forall (Ξ“ : t_ctx) (a b : ty0) (v : val Ξ“ a), v_ren_proper_P Ξ“ a v -> forall Ο€ : ev_ctx Ξ“ b, e_ren_proper_P Ξ“ b Ο€ -> e_ren_proper_P Ξ“ (a β†’ b) (K2 v Ο€)

forall (Ξ“ : t_ctx) (a : ty0) (v : val Ξ“ a), (forall (Ξ” : t_ctx) (f1 f2 : Ξ“ βŠ† Ξ”), f1 ≑ₐ f2 -> v α΅₯βŠ›α΅£ f1 = v α΅₯βŠ›α΅£ f2) -> forall (Ξ” : t_ctx) (f1 f2 : Ξ“ βŠ† Ξ”), f1 ≑ₐ f2 -> v β‚œβŠ›α΅£ f1 = v β‚œβŠ›α΅£ f2

forall (Ξ“ : t_ctx) (a b : ty0) (t1 : term Ξ“ (a β†’ b)), (forall (Ξ” : t_ctx) (f1 f2 : Ξ“ βŠ† Ξ”), f1 ≑ₐ f2 -> t1 β‚œβŠ›α΅£ f1 = t1 β‚œβŠ›α΅£ f2) -> forall t2 : term Ξ“ a, (forall (Ξ” : t_ctx) (f1 f2 : Ξ“ βŠ† Ξ”), f1 ≑ₐ f2 -> t2 β‚œβŠ›α΅£ f1 = t2 β‚œβŠ›α΅£ f2) -> forall (Ξ” : t_ctx) (f1 f2 : Ξ“ βŠ† Ξ”), f1 ≑ₐ f2 -> App t1 t2 β‚œβŠ›α΅£ f1 = App t1 t2 β‚œβŠ›α΅£ f2

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ + a) (Ξ” : t_ctx) (f1 f2 : Ξ“ βŠ† Ξ”), f1 ≑ₐ f2 -> Var i α΅₯βŠ›α΅£ f1 = Var i α΅₯βŠ›α΅£ f2

forall (Ξ“ Ξ” : t_ctx) (f1 f2 : Ξ“ βŠ† Ξ”), f1 ≑ₐ f2 -> TT α΅₯βŠ›α΅£ f1 = TT α΅₯βŠ›α΅£ f2

forall (Ξ“ : t_ctx) (a b : ty0) (t : term (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b), (forall (Ξ” : t_ctx) (f1 f2 : (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) βŠ† Ξ”), f1 ≑ₐ f2 -> t β‚œβŠ›α΅£ f1 = t β‚œβŠ›α΅£ f2) -> forall (Ξ” : t_ctx) (f1 f2 : Ξ“ βŠ† Ξ”), f1 ≑ₐ f2 -> Lam t α΅₯βŠ›α΅£ f1 = Lam t α΅₯βŠ›α΅£ f2

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ Β¬ a) (Ξ” : t_ctx) (f1 f2 : Ξ“ βŠ† Ξ”), f1 ≑ₐ f2 -> K0 i β‚‘βŠ›α΅£ f1 = K0 i β‚‘βŠ›α΅£ f2

forall (Ξ“ : t_ctx) (a b : ty0) (t : term Ξ“ (a β†’ b)), (forall (Ξ” : t_ctx) (f1 f2 : Ξ“ βŠ† Ξ”), f1 ≑ₐ f2 -> t β‚œβŠ›α΅£ f1 = t β‚œβŠ›α΅£ f2) -> forall Ο€ : ev_ctx Ξ“ b, (forall (Ξ” : t_ctx) (f1 f2 : Ξ“ βŠ† Ξ”), f1 ≑ₐ f2 -> Ο€ β‚‘βŠ›α΅£ f1 = Ο€ β‚‘βŠ›α΅£ f2) -> forall (Ξ” : t_ctx) (f1 f2 : Ξ“ βŠ† Ξ”), f1 ≑ₐ f2 -> K1 t Ο€ β‚‘βŠ›α΅£ f1 = K1 t Ο€ β‚‘βŠ›α΅£ f2

forall (Ξ“ : t_ctx) (a b : ty0) (v : val Ξ“ a), (forall (Ξ” : t_ctx) (f1 f2 : Ξ“ βŠ† Ξ”), f1 ≑ₐ f2 -> v α΅₯βŠ›α΅£ f1 = v α΅₯βŠ›α΅£ f2) -> forall Ο€ : ev_ctx Ξ“ b, (forall (Ξ” : t_ctx) (f1 f2 : Ξ“ βŠ† Ξ”), f1 ≑ₐ f2 -> Ο€ β‚‘βŠ›α΅£ f1 = Ο€ β‚‘βŠ›α΅£ f2) -> forall (Ξ” : t_ctx) (f1 f2 : Ξ“ βŠ† Ξ”), f1 ≑ₐ f2 -> K2 v Ο€ β‚‘βŠ›α΅£ f1 = K2 v Ο€ β‚‘βŠ›α΅£ f2
Ξ“: t_ctx
a: ty0
i: Ξ“ βˆ‹ + a
Ξ”: t_ctx
f1, f2: Ξ“ βŠ† Ξ”
H: f1 ≑ₐ f2

f1 + a i = f2 + a i
Ξ“: t_ctx
a, b: ty0
t: term (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b
H: forall (Ξ” : t_ctx) (f1 f2 : (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) βŠ† Ξ”), f1 ≑ₐ f2 -> t β‚œβŠ›α΅£ f1 = t β‚œβŠ›α΅£ f2
Ξ”: t_ctx
f1, f2: Ξ“ βŠ† Ξ”
H0: f1 ≑ₐ f2
t β‚œβŠ›α΅£ r_shift2 f1 = t β‚œβŠ›α΅£ r_shift2 f2
Ξ“: t_ctx
a: ty0
i: Ξ“ βˆ‹ Β¬ a
Ξ”: t_ctx
f1, f2: Ξ“ βŠ† Ξ”
H: f1 ≑ₐ f2
f1 Β¬ a i = f2 Β¬ a i
all: eapply H; now apply r_shift2_eq. Qed.
Ξ“, Ξ”: t_ctx

Proper (asgn_eq Ξ“ Ξ” ==> (βˆ€β‚• a, eq ==> eq)) t_rename
intros f1 f2 H1 a x y ->; now apply (term_ind_mut _ _ _ ren_proper_prf). Qed.
Ξ“, Ξ”: t_ctx

Proper (asgn_eq Ξ“ Ξ” ==> (βˆ€β‚• a, eq ==> eq)) v_rename
intros f1 f2 H1 a x y ->; now apply (val_ind_mut _ _ _ ren_proper_prf). Qed.
Ξ“, Ξ”: t_ctx

Proper (asgn_eq Ξ“ Ξ” ==> (βˆ€β‚• a, eq ==> eq)) e_rename
intros f1 f2 H1 a x y ->; now apply (ctx_ind_mut _ _ _ ren_proper_prf). Qed.
Ξ“, Ξ”: t_ctx

Proper (asgn_eq Ξ“ Ξ” ==> (βˆ€β‚• a, eq ==> eq)) m_rename
intros ? ? H1 [] _ ? ->; cbn in *; now rewrite H1. Qed.
Ξ“, Ξ”: t_ctx

Proper (asgn_eq Ξ“ Ξ” ==> eq ==> eq) s_rename
intros ? ? H1 _ x ->; destruct x; unfold s_rename; cbn; now rewrite H1. Qed.
Ξ“1, Ξ“2, Ξ“3: t_ctx

Proper (asgn_eq Ξ“2 Ξ“3 ==> asgn_eq Ξ“1 Ξ“2 ==> asgn_eq Ξ“1 Ξ“3) a_ren
intros ? ? H1 ? ? H2 ? ?; apply (m_ren_eq _ _ H1), H2. Qed.
Ξ“, Ξ”: t_ctx
x, y: ty

Proper (asgn_eq Ξ“ Ξ” ==> asgn_eq (Ξ“ β–Άβ‚“ x β–Άβ‚“ y) (Ξ” β–Άβ‚“ x β–Άβ‚“ y)) a_shift2
Ξ“, Ξ”: t_ctx
x, y: ty
x0, y0: Ξ“ =[ val_m ]> Ξ”
H: x0 ≑ₐ y0
a: ty
v: Ξ“ β–Άβ‚“ x β–Άβ‚“ y βˆ‹ a

a_shift2 x0 a v = a_shift2 y0 a v
Ξ“1: ctx ty
Ξ”: t_ctx
y, y1: ty
x0, y0: Ξ“1 =[ val_m ]> Ξ”
H: x0 ≑ₐ y0
a: ty
v: var a Ξ“1

m_shift2 a (x0 a v) = m_shift2 a (y0 a v)
now rewrite H. Qed.

Lemma 2: renaming-renaming assocativity. I say "associativity" because it definitely looks like associativity if we disregard the subscripts. More precisely it could be described as the composition law a right action.

Definition t_ren_ren_P Ξ“1 a (t : term Ξ“1 a) : Prop :=
  forall Ξ“2 Ξ“3 (f1 : Ξ“1 βŠ† Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3),
    (t β‚œβŠ›α΅£ f1) β‚œβŠ›α΅£ f2 = t β‚œβŠ›α΅£ (f1 α΅£βŠ› f2).
Definition v_ren_ren_P Ξ“1 a (v : val Ξ“1 a) : Prop :=
  forall Ξ“2 Ξ“3 (f1 : Ξ“1 βŠ† Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3),
    (v α΅₯βŠ›α΅£ f1) α΅₯βŠ›α΅£ f2 = v α΅₯βŠ›α΅£ (f1 α΅£βŠ› f2).
Definition e_ren_ren_P Ξ“1 a (Ο€ : ev_ctx Ξ“1 a) : Prop :=
  forall Ξ“2 Ξ“3 (f1 : Ξ“1 βŠ† Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3),
    (Ο€ β‚‘βŠ›α΅£ f1) β‚‘βŠ›α΅£ f2 = Ο€ β‚‘βŠ›α΅£ (f1 α΅£βŠ› f2).


syn_ind_args t_ren_ren_P v_ren_ren_P e_ren_ren_P

forall (Ξ“ : t_ctx) (a : ty0) (v : val Ξ“ a), v_ren_ren_P Ξ“ a v -> t_ren_ren_P Ξ“ a v

forall (Ξ“ : t_ctx) (a b : ty0) (t1 : term Ξ“ (a β†’ b)), t_ren_ren_P Ξ“ (a β†’ b) t1 -> forall t2 : term Ξ“ a, t_ren_ren_P Ξ“ a t2 -> t_ren_ren_P Ξ“ b (App t1 t2)

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ + a), v_ren_ren_P Ξ“ a (Var i)

forall Ξ“ : t_ctx, v_ren_ren_P Ξ“ ΞΉ TT

forall (Ξ“ : t_ctx) (a b : ty0) (t : term (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b), t_ren_ren_P (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b t -> v_ren_ren_P Ξ“ (a β†’ b) (Lam t)

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ Β¬ a), e_ren_ren_P Ξ“ a (K0 i)

forall (Ξ“ : t_ctx) (a b : ty0) (t : term Ξ“ (a β†’ b)), t_ren_ren_P Ξ“ (a β†’ b) t -> forall Ο€ : ev_ctx Ξ“ b, e_ren_ren_P Ξ“ b Ο€ -> e_ren_ren_P Ξ“ a (K1 t Ο€)

forall (Ξ“ : t_ctx) (a b : ty0) (v : val Ξ“ a), v_ren_ren_P Ξ“ a v -> forall Ο€ : ev_ctx Ξ“ b, e_ren_ren_P Ξ“ b Ο€ -> e_ren_ren_P Ξ“ (a β†’ b) (K2 v Ο€)

forall (Ξ“ : t_ctx) (a : ty0) (v : val Ξ“ a), (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (v α΅₯βŠ›α΅£ f1) α΅₯βŠ›α΅£ f2 = v α΅₯βŠ›α΅£ (f1 α΅£βŠ› f2)) -> forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (v β‚œβŠ›α΅£ f1) β‚œβŠ›α΅£ f2 = v β‚œβŠ›α΅£ (f1 α΅£βŠ› f2)

forall (Ξ“ : t_ctx) (a b : ty0) (t1 : term Ξ“ (a β†’ b)), (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (t1 β‚œβŠ›α΅£ f1) β‚œβŠ›α΅£ f2 = t1 β‚œβŠ›α΅£ (f1 α΅£βŠ› f2)) -> forall t2 : term Ξ“ a, (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (t2 β‚œβŠ›α΅£ f1) β‚œβŠ›α΅£ f2 = t2 β‚œβŠ›α΅£ (f1 α΅£βŠ› f2)) -> forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (App t1 t2 β‚œβŠ›α΅£ f1) β‚œβŠ›α΅£ f2 = App t1 t2 β‚œβŠ›α΅£ (f1 α΅£βŠ› f2)

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ + a) (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (Var i α΅₯βŠ›α΅£ f1) α΅₯βŠ›α΅£ f2 = Var i α΅₯βŠ›α΅£ (f1 α΅£βŠ› f2)

forall (Ξ“ Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (TT α΅₯βŠ›α΅£ f1) α΅₯βŠ›α΅£ f2 = TT α΅₯βŠ›α΅£ (f1 α΅£βŠ› f2)

forall (Ξ“ : t_ctx) (a b : ty0) (t : term (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b), (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) βŠ† Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (t β‚œβŠ›α΅£ f1) β‚œβŠ›α΅£ f2 = t β‚œβŠ›α΅£ (f1 α΅£βŠ› f2)) -> forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (Lam t α΅₯βŠ›α΅£ f1) α΅₯βŠ›α΅£ f2 = Lam t α΅₯βŠ›α΅£ (f1 α΅£βŠ› f2)

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ Β¬ a) (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (K0 i β‚‘βŠ›α΅£ f1) β‚‘βŠ›α΅£ f2 = K0 i β‚‘βŠ›α΅£ (f1 α΅£βŠ› f2)

forall (Ξ“ : t_ctx) (a b : ty0) (t : term Ξ“ (a β†’ b)), (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (t β‚œβŠ›α΅£ f1) β‚œβŠ›α΅£ f2 = t β‚œβŠ›α΅£ (f1 α΅£βŠ› f2)) -> forall Ο€ : ev_ctx Ξ“ b, (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (Ο€ β‚‘βŠ›α΅£ f1) β‚‘βŠ›α΅£ f2 = Ο€ β‚‘βŠ›α΅£ (f1 α΅£βŠ› f2)) -> forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (K1 t Ο€ β‚‘βŠ›α΅£ f1) β‚‘βŠ›α΅£ f2 = K1 t Ο€ β‚‘βŠ›α΅£ (f1 α΅£βŠ› f2)

forall (Ξ“ : t_ctx) (a b : ty0) (v : val Ξ“ a), (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (v α΅₯βŠ›α΅£ f1) α΅₯βŠ›α΅£ f2 = v α΅₯βŠ›α΅£ (f1 α΅£βŠ› f2)) -> forall Ο€ : ev_ctx Ξ“ b, (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (Ο€ β‚‘βŠ›α΅£ f1) β‚‘βŠ›α΅£ f2 = Ο€ β‚‘βŠ›α΅£ (f1 α΅£βŠ› f2)) -> forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (K2 v Ο€ β‚‘βŠ›α΅£ f1) β‚‘βŠ›α΅£ f2 = K2 v Ο€ β‚‘βŠ›α΅£ (f1 α΅£βŠ› f2)
Ξ“: t_ctx
a, b: ty0
t: term (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b
H: forall (Ξ“2 Ξ“3 : t_ctx) (f1 : (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) βŠ† Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (t β‚œβŠ›α΅£ f1) β‚œβŠ›α΅£ f2 = t β‚œβŠ›α΅£ (f1 α΅£βŠ› f2)
Ξ“2, Ξ“3: t_ctx
f1: Ξ“ βŠ† Ξ“2
f2: Ξ“2 βŠ† Ξ“3

(t β‚œβŠ›α΅£ r_shift2 f1) β‚œβŠ›α΅£ r_shift2 f2 = t β‚œβŠ›α΅£ r_shift2 (f1 α΅£βŠ› f2)
Ξ“: t_ctx
a, b: ty0
t: term (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b
H: forall (Ξ“2 Ξ“3 : t_ctx) (f1 : (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) βŠ† Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (t β‚œβŠ›α΅£ f1) β‚œβŠ›α΅£ f2 = t β‚œβŠ›α΅£ (f1 α΅£βŠ› f2)
Ξ“2, Ξ“3: t_ctx
f1: Ξ“ βŠ† Ξ“2
f2: Ξ“2 βŠ† Ξ“3

r_shift2 f1 α΅£βŠ› r_shift2 f2 ≑ₐ r_shift2 (f1 α΅£βŠ› f2)
intros ? v; now do 2 (dependent elimination v; eauto). Qed.
Ξ“1, Ξ“2, Ξ“3: ctx ty
f1: Ξ“1 βŠ† Ξ“2
f2: Ξ“2 βŠ† Ξ“3
a: ty0
t: term Ξ“1 a

(t β‚œβŠ›α΅£ f1) β‚œβŠ›α΅£ f2 = t β‚œβŠ›α΅£ (f1 α΅£βŠ› f2)
now apply (term_ind_mut _ _ _ ren_ren_prf). Qed.
Ξ“1, Ξ“2, Ξ“3: ctx ty
f1: Ξ“1 βŠ† Ξ“2
f2: Ξ“2 βŠ† Ξ“3
a: ty0
v: val Ξ“1 a

(v α΅₯βŠ›α΅£ f1) α΅₯βŠ›α΅£ f2 = v α΅₯βŠ›α΅£ (f1 α΅£βŠ› f2)
now apply (val_ind_mut _ _ _ ren_ren_prf). Qed.
Ξ“1, Ξ“2, Ξ“3: ctx ty
f1: Ξ“1 βŠ† Ξ“2
f2: Ξ“2 βŠ† Ξ“3
a: ty0
Ο€: ev_ctx Ξ“1 a

(Ο€ β‚‘βŠ›α΅£ f1) β‚‘βŠ›α΅£ f2 = Ο€ β‚‘βŠ›α΅£ (f1 α΅£βŠ› f2)
now apply (ctx_ind_mut _ _ _ ren_ren_prf). Qed.
Ξ“1, Ξ“2, Ξ“3: ctx ty
f1: Ξ“1 βŠ† Ξ“2
f2: Ξ“2 βŠ† Ξ“3
a: ty
v: val_m Ξ“1 a

(v β‚˜βŠ›α΅£ f1) β‚˜βŠ›α΅£ f2 = v β‚˜βŠ›α΅£ (f1 α΅£βŠ› f2)
destruct a; [ now apply v_ren_ren | now apply e_ren_ren ]. Qed.
Ξ“1, Ξ“2, Ξ“3: ctx ty
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.

Lemma 3: left identity law of renaming.

Definition t_ren_id_l_P Ξ“ a (t : term Ξ“ a) : Prop := t β‚œβŠ›α΅£ r_id = t .
Definition v_ren_id_l_P Ξ“ a (v : val Ξ“ a) : Prop := v α΅₯βŠ›α΅£ r_id = v .
Definition e_ren_id_l_P Ξ“ a (Ο€ : ev_ctx Ξ“ a) : Prop := Ο€ β‚‘βŠ›α΅£ r_id = Ο€.

syn_ind_args t_ren_id_l_P v_ren_id_l_P e_ren_id_l_P

forall (Ξ“ : t_ctx) (a : ty0) (v : val Ξ“ a), v_ren_id_l_P Ξ“ a v -> t_ren_id_l_P Ξ“ a v

forall (Ξ“ : t_ctx) (a b : ty0) (t1 : term Ξ“ (a β†’ b)), t_ren_id_l_P Ξ“ (a β†’ b) t1 -> forall t2 : term Ξ“ a, t_ren_id_l_P Ξ“ a t2 -> t_ren_id_l_P Ξ“ b (App t1 t2)

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ + a), v_ren_id_l_P Ξ“ a (Var i)

forall Ξ“ : t_ctx, v_ren_id_l_P Ξ“ ΞΉ TT

forall (Ξ“ : t_ctx) (a b : ty0) (t : term (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b), t_ren_id_l_P (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b t -> v_ren_id_l_P Ξ“ (a β†’ b) (Lam t)

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ Β¬ a), e_ren_id_l_P Ξ“ a (K0 i)

forall (Ξ“ : t_ctx) (a b : ty0) (t : term Ξ“ (a β†’ b)), t_ren_id_l_P Ξ“ (a β†’ b) t -> forall Ο€ : ev_ctx Ξ“ b, e_ren_id_l_P Ξ“ b Ο€ -> e_ren_id_l_P Ξ“ a (K1 t Ο€)

forall (Ξ“ : t_ctx) (a b : ty0) (v : val Ξ“ a), v_ren_id_l_P Ξ“ a v -> forall Ο€ : ev_ctx Ξ“ b, e_ren_id_l_P Ξ“ b Ο€ -> e_ren_id_l_P Ξ“ (a β†’ b) (K2 v Ο€)

forall (Ξ“ : t_ctx) (a : ty0) (v : val Ξ“ a), v α΅₯βŠ›α΅£ r_id = v -> v β‚œβŠ›α΅£ r_id = v

forall (Ξ“ : t_ctx) (a b : ty0) (t1 : term Ξ“ (a β†’ b)), t1 β‚œβŠ›α΅£ r_id = t1 -> forall t2 : term Ξ“ a, t2 β‚œβŠ›α΅£ r_id = t2 -> App t1 t2 β‚œβŠ›α΅£ r_id = App t1 t2

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ + a), Var i α΅₯βŠ›α΅£ r_id = Var i

forall Ξ“ : t_ctx, TT α΅₯βŠ›α΅£ r_id = TT

forall (Ξ“ : t_ctx) (a b : ty0) (t : term (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b), t β‚œβŠ›α΅£ r_id = t -> Lam t α΅₯βŠ›α΅£ r_id = Lam t

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ Β¬ a), K0 i β‚‘βŠ›α΅£ r_id = K0 i

forall (Ξ“ : t_ctx) (a b : ty0) (t : term Ξ“ (a β†’ b)), t β‚œβŠ›α΅£ r_id = t -> forall Ο€ : ev_ctx Ξ“ b, Ο€ β‚‘βŠ›α΅£ r_id = Ο€ -> K1 t Ο€ β‚‘βŠ›α΅£ r_id = K1 t Ο€

forall (Ξ“ : t_ctx) (a b : ty0) (v : val Ξ“ a), v α΅₯βŠ›α΅£ r_id = v -> forall Ο€ : ev_ctx Ξ“ b, Ο€ β‚‘βŠ›α΅£ r_id = Ο€ -> K2 v Ο€ β‚‘βŠ›α΅£ r_id = K2 v Ο€
Ξ“: t_ctx
a, b: ty0
t: term (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b
H: t β‚œβŠ›α΅£ r_id = t

t β‚œβŠ›α΅£ r_shift2 r_id = t
Ξ“: t_ctx
a, b: ty0
t: term (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b
H: t β‚œβŠ›α΅£ r_id = t

r_shift2 r_id ≑ₐ r_id
intros ? v; now do 2 (dependent elimination v; eauto). Qed.
Ξ“: t_ctx
a: ty0
t: term Ξ“ a

t β‚œβŠ›α΅£ r_id = t
now apply (term_ind_mut _ _ _ ren_id_l_prf). Qed.
Ξ“: t_ctx
a: ty0
v: val Ξ“ a

v α΅₯βŠ›α΅£ r_id = v
now apply (val_ind_mut _ _ _ ren_id_l_prf). Qed.
Ξ“: t_ctx
a: ty0
Ο€: ev_ctx Ξ“ a

Ο€ β‚‘βŠ›α΅£ r_id = Ο€
now apply (ctx_ind_mut _ _ _ ren_id_l_prf). Qed.
Ξ“: t_ctx
a: ty
v: val_m Ξ“ a

v β‚˜βŠ›α΅£ r_id = v
destruct a; [ now apply v_ren_id_l | now apply e_ren_id_l ]. Qed.
Ξ“: t_ctx
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 4: right identity law of renaming. This one basically holds definitionally, it only needs a case split for some of the derived notions. We will also prove a consequence on weakenings: identity law.

Ξ“, Ξ”: ctx ty
f: Ξ“ βŠ† Ξ”
a: ty
i: Ξ“ βˆ‹ a

a_id a i β‚˜βŠ›α΅£ f = a_id a (f a i)
now destruct a. Qed.
Ξ“, Ξ”: ctx ty
f: Ξ“ βŠ† Ξ”

a_id βŠ›α΅£ f ≑ₐ f α΅£βŠ› a_id
intros ??; now apply m_ren_id_r. Qed.
Ξ“: t_ctx
x, y: ty

a_shift2 a_id ≑ₐ a_id
Ξ“1: ctx ty
y, y0, a: ty
v: var a Ξ“1

a_shift2 a_id a (pop (pop v)) = a_id a (pop (pop v))
exact (m_ren_id_r _ _). Qed.

Lemma 5: shifting assigments commutes with left and right renaming.

Ξ“1, Ξ“2: ctx ty
Ξ“3: t_ctx
a, b: ty
f1: Ξ“1 βŠ† Ξ“2
f2: Ξ“2 =[ val_m ]> Ξ“3

a_shift2 (f1 α΅£βŠ› f2) ≑ₐ r_shift2 f1 α΅£βŠ› a_shift2 f2
intros ? v; do 2 (dependent elimination v; auto). Qed.
Ξ“1, Ξ“2, Ξ“3: t_ctx
a, b: ty
f1: Ξ“1 =[ val_m ]> Ξ“2
f2: Ξ“2 βŠ† Ξ“3

a_shift2 (f1 βŠ›α΅£ f2) ≑ₐ a_shift2 f1 βŠ›α΅£ r_shift2 f2
Ξ“1, Ξ“2, Ξ“3: t_ctx
a, b: ty
f1: Ξ“1 =[ val_m ]> Ξ“2
f2: Ξ“2 βŠ† Ξ“3
a0: ty
v: Ξ“1 β–Άβ‚“ a β–Άβ‚“ b βˆ‹ a0

a_shift2 (f1 βŠ›α΅£ f2) a0 v = (a_shift2 f1 βŠ›α΅£ r_shift2 f2)%asgn a0 v
Ξ“0: ctx ty
Ξ“2, Ξ“3: t_ctx
y0, y: ty
f1: Ξ“0 =[ val_m ]> Ξ“2
f2: Ξ“2 βŠ† Ξ“3
a0: ty
v: var a0 Ξ“0

m_shift2 a0 ((f1 βŠ›α΅£ f2)%asgn a0 v) = (a_shift2 f1 βŠ›α΅£ r_shift2 f2)%asgn a0 (pop (pop v))
unfold a_ren; cbn - [ m_rename ]; unfold m_shift2; now rewrite 2 m_ren_ren. Qed.

Lemma 6: substitution respects pointwise equality of assigments.

Definition t_sub_proper_P Ξ“ a (t : term Ξ“ a) : Prop :=
  forall Ξ” (f1 f2 : Ξ“ =[val_m]> Ξ”), f1 ≑ₐ f2 -> t `β‚œβŠ› f1 = t `β‚œβŠ› f2 .
Definition v_sub_proper_P Ξ“ a (v : val Ξ“ a) : Prop :=
  forall Ξ” (f1 f2 : Ξ“ =[val_m]> Ξ”), f1 ≑ₐ f2 -> v `α΅₯βŠ› f1 = v `α΅₯βŠ› f2 .
Definition e_sub_proper_P Ξ“ a (Ο€ : ev_ctx Ξ“ a) : Prop :=
  forall Ξ” (f1 f2 : Ξ“ =[val_m]> Ξ”), f1 ≑ₐ f2 -> Ο€ `β‚‘βŠ› f1 = Ο€ `β‚‘βŠ› f2.

syn_ind_args t_sub_proper_P v_sub_proper_P e_sub_proper_P

forall (Ξ“ : t_ctx) (a : ty0) (v : val Ξ“ a), v_sub_proper_P Ξ“ a v -> t_sub_proper_P Ξ“ a v

forall (Ξ“ : t_ctx) (a b : ty0) (t1 : term Ξ“ (a β†’ b)), t_sub_proper_P Ξ“ (a β†’ b) t1 -> forall t2 : term Ξ“ a, t_sub_proper_P Ξ“ a t2 -> t_sub_proper_P Ξ“ b (App t1 t2)

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ + a), v_sub_proper_P Ξ“ a (Var i)

forall Ξ“ : t_ctx, v_sub_proper_P Ξ“ ΞΉ TT

forall (Ξ“ : t_ctx) (a b : ty0) (t : term (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b), t_sub_proper_P (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b t -> v_sub_proper_P Ξ“ (a β†’ b) (Lam t)

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ Β¬ a), e_sub_proper_P Ξ“ a (K0 i)

forall (Ξ“ : t_ctx) (a b : ty0) (t : term Ξ“ (a β†’ b)), t_sub_proper_P Ξ“ (a β†’ b) t -> forall Ο€ : ev_ctx Ξ“ b, e_sub_proper_P Ξ“ b Ο€ -> e_sub_proper_P Ξ“ a (K1 t Ο€)

forall (Ξ“ : t_ctx) (a b : ty0) (v : val Ξ“ a), v_sub_proper_P Ξ“ a v -> forall Ο€ : ev_ctx Ξ“ b, e_sub_proper_P Ξ“ b Ο€ -> e_sub_proper_P Ξ“ (a β†’ b) (K2 v Ο€)

forall (Ξ“ : t_ctx) (a : ty0) (v : val Ξ“ a), (forall (Ξ” : t_ctx) (f1 f2 : Ξ“ =[ val_m ]> Ξ”), f1 ≑ₐ f2 -> v `α΅₯βŠ› f1 = v `α΅₯βŠ› f2) -> forall (Ξ” : t_ctx) (f1 f2 : Ξ“ =[ val_m ]> Ξ”), f1 ≑ₐ f2 -> v `β‚œβŠ› f1 = v `β‚œβŠ› f2

forall (Ξ“ : t_ctx) (a b : ty0) (t1 : term Ξ“ (a β†’ b)), (forall (Ξ” : t_ctx) (f1 f2 : Ξ“ =[ val_m ]> Ξ”), f1 ≑ₐ f2 -> t1 `β‚œβŠ› f1 = t1 `β‚œβŠ› f2) -> forall t2 : term Ξ“ a, (forall (Ξ” : t_ctx) (f1 f2 : Ξ“ =[ val_m ]> Ξ”), f1 ≑ₐ f2 -> t2 `β‚œβŠ› f1 = t2 `β‚œβŠ› f2) -> forall (Ξ” : t_ctx) (f1 f2 : Ξ“ =[ val_m ]> Ξ”), f1 ≑ₐ f2 -> App t1 t2 `β‚œβŠ› f1 = App t1 t2 `β‚œβŠ› f2

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ + a) (Ξ” : t_ctx) (f1 f2 : Ξ“ =[ val_m ]> Ξ”), f1 ≑ₐ f2 -> Var i `α΅₯βŠ› f1 = Var i `α΅₯βŠ› f2

forall (Ξ“ Ξ” : t_ctx) (f1 f2 : Ξ“ =[ val_m ]> Ξ”), f1 ≑ₐ f2 -> TT `α΅₯βŠ› f1 = TT `α΅₯βŠ› f2

forall (Ξ“ : t_ctx) (a b : ty0) (t : term (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b), (forall (Ξ” : t_ctx) (f1 f2 : (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) =[ val_m ]> Ξ”), f1 ≑ₐ f2 -> t `β‚œβŠ› f1 = t `β‚œβŠ› f2) -> forall (Ξ” : t_ctx) (f1 f2 : Ξ“ =[ val_m ]> Ξ”), f1 ≑ₐ f2 -> Lam t `α΅₯βŠ› f1 = Lam t `α΅₯βŠ› f2

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ Β¬ a) (Ξ” : t_ctx) (f1 f2 : Ξ“ =[ val_m ]> Ξ”), f1 ≑ₐ f2 -> K0 i `β‚‘βŠ› f1 = K0 i `β‚‘βŠ› f2

forall (Ξ“ : t_ctx) (a b : ty0) (t : term Ξ“ (a β†’ b)), (forall (Ξ” : t_ctx) (f1 f2 : Ξ“ =[ val_m ]> Ξ”), f1 ≑ₐ f2 -> t `β‚œβŠ› f1 = t `β‚œβŠ› f2) -> forall Ο€ : ev_ctx Ξ“ b, (forall (Ξ” : t_ctx) (f1 f2 : Ξ“ =[ val_m ]> Ξ”), f1 ≑ₐ f2 -> Ο€ `β‚‘βŠ› f1 = Ο€ `β‚‘βŠ› f2) -> forall (Ξ” : t_ctx) (f1 f2 : Ξ“ =[ val_m ]> Ξ”), f1 ≑ₐ f2 -> K1 t Ο€ `β‚‘βŠ› f1 = K1 t Ο€ `β‚‘βŠ› f2

forall (Ξ“ : t_ctx) (a b : ty0) (v : val Ξ“ a), (forall (Ξ” : t_ctx) (f1 f2 : Ξ“ =[ val_m ]> Ξ”), f1 ≑ₐ f2 -> v `α΅₯βŠ› f1 = v `α΅₯βŠ› f2) -> forall Ο€ : ev_ctx Ξ“ b, (forall (Ξ” : t_ctx) (f1 f2 : Ξ“ =[ val_m ]> Ξ”), f1 ≑ₐ f2 -> Ο€ `β‚‘βŠ› f1 = Ο€ `β‚‘βŠ› f2) -> forall (Ξ” : t_ctx) (f1 f2 : Ξ“ =[ val_m ]> Ξ”), f1 ≑ₐ f2 -> K2 v Ο€ `β‚‘βŠ› f1 = K2 v Ο€ `β‚‘βŠ› f2
Ξ“: t_ctx
a, b: ty0
t: term (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b
H: forall (Ξ” : t_ctx) (f1 f2 : (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) =[ val_m ]> Ξ”), f1 ≑ₐ f2 -> t `β‚œβŠ› f1 = t `β‚œβŠ› f2
Ξ”: t_ctx
f1, f2: Ξ“ =[ val_m ]> Ξ”
H0: f1 ≑ₐ f2

t `β‚œβŠ› a_shift2 f1 = t `β‚œβŠ› a_shift2 f2
now apply H, a_shift2_eq. Qed.
Ξ“, Ξ”: t_ctx

Proper (asgn_eq Ξ“ Ξ” ==> (βˆ€β‚• a, eq ==> eq)) t_subst
intros ? ? H1 a _ ? ->; now apply (term_ind_mut _ _ _ sub_proper_prf). Qed.
Ξ“, Ξ”: t_ctx

Proper (asgn_eq Ξ“ Ξ” ==> (βˆ€β‚• a, eq ==> eq)) v_subst
intros ? ? H1 a _ ? ->; now apply (val_ind_mut _ _ _ sub_proper_prf). Qed.
Ξ“, Ξ”: t_ctx

Proper (asgn_eq Ξ“ Ξ” ==> (βˆ€β‚• a, eq ==> eq)) e_subst
intros ? ? H1 a _ ? ->; now apply (ctx_ind_mut _ _ _ sub_proper_prf). Qed.

Proper (βˆ€β‚• Ξ“, (βˆ€β‚• a, eq ==> (βˆ€β‚• Ξ”, asgn_eq Ξ“ Ξ” ==> eq))) m_subst
intros ? [] ?? -> ??? H1; cbn in *; now rewrite H1. Qed.

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 a (t : term Ξ“1 a) : Prop :=
  forall Ξ“2 Ξ“3 (f1 : Ξ“1 =[val_m]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3) ,
    (t `β‚œβŠ› f1) β‚œβŠ›α΅£ f2 = t `β‚œβŠ› (f1 βŠ›α΅£ f2) .
Definition v_ren_sub_P Ξ“1 a (v : val Ξ“1 a) : Prop :=
  forall Ξ“2 Ξ“3 (f1 : Ξ“1 =[val_m]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3) ,
    (v `α΅₯βŠ› f1) α΅₯βŠ›α΅£ f2 = v `α΅₯βŠ› (f1 βŠ›α΅£ f2) .
Definition e_ren_sub_P Ξ“1 a (Ο€ : ev_ctx Ξ“1 a) : Prop :=
  forall Ξ“2 Ξ“3 (f1 : Ξ“1 =[val_m]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3) ,
    (Ο€ `β‚‘βŠ› f1) β‚‘βŠ›α΅£ f2 = Ο€ `β‚‘βŠ› (f1 βŠ›α΅£ f2) .

syn_ind_args t_ren_sub_P v_ren_sub_P e_ren_sub_P

forall (Ξ“ : t_ctx) (a : ty0) (v : val Ξ“ a), v_ren_sub_P Ξ“ a v -> t_ren_sub_P Ξ“ a v

forall (Ξ“ : t_ctx) (a b : ty0) (t1 : term Ξ“ (a β†’ b)), t_ren_sub_P Ξ“ (a β†’ b) t1 -> forall t2 : term Ξ“ a, t_ren_sub_P Ξ“ a t2 -> t_ren_sub_P Ξ“ b (App t1 t2)

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ + a), v_ren_sub_P Ξ“ a (Var i)

forall Ξ“ : t_ctx, v_ren_sub_P Ξ“ ΞΉ TT

forall (Ξ“ : t_ctx) (a b : ty0) (t : term (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b), t_ren_sub_P (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b t -> v_ren_sub_P Ξ“ (a β†’ b) (Lam t)

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ Β¬ a), e_ren_sub_P Ξ“ a (K0 i)

forall (Ξ“ : t_ctx) (a b : ty0) (t : term Ξ“ (a β†’ b)), t_ren_sub_P Ξ“ (a β†’ b) t -> forall Ο€ : ev_ctx Ξ“ b, e_ren_sub_P Ξ“ b Ο€ -> e_ren_sub_P Ξ“ a (K1 t Ο€)

forall (Ξ“ : t_ctx) (a b : ty0) (v : val Ξ“ a), v_ren_sub_P Ξ“ a v -> forall Ο€ : ev_ctx Ξ“ b, e_ren_sub_P Ξ“ b Ο€ -> e_ren_sub_P Ξ“ (a β†’ b) (K2 v Ο€)

forall (Ξ“ : t_ctx) (a : ty0) (v : val Ξ“ a), (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (v `α΅₯βŠ› f1) α΅₯βŠ›α΅£ f2 = v `α΅₯βŠ› f1 βŠ›α΅£ f2) -> forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (v `β‚œβŠ› f1) β‚œβŠ›α΅£ f2 = v `β‚œβŠ› f1 βŠ›α΅£ f2

forall (Ξ“ : t_ctx) (a b : ty0) (t1 : term Ξ“ (a β†’ b)), (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (t1 `β‚œβŠ› f1) β‚œβŠ›α΅£ f2 = t1 `β‚œβŠ› f1 βŠ›α΅£ f2) -> forall t2 : term Ξ“ a, (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (t2 `β‚œβŠ› f1) β‚œβŠ›α΅£ f2 = t2 `β‚œβŠ› f1 βŠ›α΅£ f2) -> forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (App t1 t2 `β‚œβŠ› f1) β‚œβŠ›α΅£ f2 = App t1 t2 `β‚œβŠ› f1 βŠ›α΅£ f2

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ + a) (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (Var i `α΅₯βŠ› f1) α΅₯βŠ›α΅£ f2 = Var i `α΅₯βŠ› f1 βŠ›α΅£ f2

forall (Ξ“ Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (TT `α΅₯βŠ› f1) α΅₯βŠ›α΅£ f2 = TT `α΅₯βŠ› f1 βŠ›α΅£ f2

forall (Ξ“ : t_ctx) (a b : ty0) (t : term (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b), (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (t `β‚œβŠ› f1) β‚œβŠ›α΅£ f2 = t `β‚œβŠ› f1 βŠ›α΅£ f2) -> forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (Lam t `α΅₯βŠ› f1) α΅₯βŠ›α΅£ f2 = Lam t `α΅₯βŠ› f1 βŠ›α΅£ f2

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ Β¬ a) (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (K0 i `β‚‘βŠ› f1) β‚‘βŠ›α΅£ f2 = K0 i `β‚‘βŠ› f1 βŠ›α΅£ f2

forall (Ξ“ : t_ctx) (a b : ty0) (t : term Ξ“ (a β†’ b)), (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (t `β‚œβŠ› f1) β‚œβŠ›α΅£ f2 = t `β‚œβŠ› f1 βŠ›α΅£ f2) -> forall Ο€ : ev_ctx Ξ“ b, (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (Ο€ `β‚‘βŠ› f1) β‚‘βŠ›α΅£ f2 = Ο€ `β‚‘βŠ› f1 βŠ›α΅£ f2) -> forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (K1 t Ο€ `β‚‘βŠ› f1) β‚‘βŠ›α΅£ f2 = K1 t Ο€ `β‚‘βŠ› f1 βŠ›α΅£ f2

forall (Ξ“ : t_ctx) (a b : ty0) (v : val Ξ“ a), (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (v `α΅₯βŠ› f1) α΅₯βŠ›α΅£ f2 = v `α΅₯βŠ› f1 βŠ›α΅£ f2) -> forall Ο€ : ev_ctx Ξ“ b, (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (Ο€ `β‚‘βŠ› f1) β‚‘βŠ›α΅£ f2 = Ο€ `β‚‘βŠ› f1 βŠ›α΅£ f2) -> forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (K2 v Ο€ `β‚‘βŠ› f1) β‚‘βŠ›α΅£ f2 = K2 v Ο€ `β‚‘βŠ› f1 βŠ›α΅£ f2
Ξ“: t_ctx
a: ty0
v: val Ξ“ a
H: forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (v `α΅₯βŠ› f1) α΅₯βŠ›α΅£ f2 = v `α΅₯βŠ› f1 βŠ›α΅£ f2
Ξ“2, Ξ“3: t_ctx
f1: Ξ“ =[ val_m ]> Ξ“2
f2: Ξ“2 βŠ† Ξ“3

(v `α΅₯βŠ› f1) α΅₯βŠ›α΅£ f2 = v `α΅₯βŠ› f1 βŠ›α΅£ f2
Ξ“: t_ctx
a, b: ty0
t1: term Ξ“ (a β†’ b)
H: forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (t1 `β‚œβŠ› f1) β‚œβŠ›α΅£ f2 = t1 `β‚œβŠ› f1 βŠ›α΅£ f2
t2: term Ξ“ a
H0: forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (t2 `β‚œβŠ› f1) β‚œβŠ›α΅£ f2 = t2 `β‚œβŠ› f1 βŠ›α΅£ f2
Ξ“2, Ξ“3: t_ctx
f1: Ξ“ =[ val_m ]> Ξ“2
f2: Ξ“2 βŠ† Ξ“3
(t1 `β‚œβŠ› f1) β‚œβŠ›α΅£ f2 = t1 `β‚œβŠ› f1 βŠ›α΅£ f2
Ξ“: t_ctx
a, b: ty0
t1: term Ξ“ (a β†’ b)
H: forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (t1 `β‚œβŠ› f1) β‚œβŠ›α΅£ f2 = t1 `β‚œβŠ› f1 βŠ›α΅£ f2
t2: term Ξ“ a
H0: forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (t2 `β‚œβŠ› f1) β‚œβŠ›α΅£ f2 = t2 `β‚œβŠ› f1 βŠ›α΅£ f2
Ξ“2, Ξ“3: t_ctx
f1: Ξ“ =[ val_m ]> Ξ“2
f2: Ξ“2 βŠ† Ξ“3
(t2 `β‚œβŠ› f1) β‚œβŠ›α΅£ f2 = t2 `β‚œβŠ› f1 βŠ›α΅£ f2
Ξ“: t_ctx
a, b: ty0
t: term (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b
H: forall (Ξ“2 Ξ“3 : t_ctx) (f1 : (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (t `β‚œβŠ› f1) β‚œβŠ›α΅£ f2 = t `β‚œβŠ› f1 βŠ›α΅£ f2
Ξ“2, Ξ“3: t_ctx
f1: Ξ“ =[ val_m ]> Ξ“2
f2: Ξ“2 βŠ† Ξ“3
(t `β‚œβŠ› a_shift2 f1) β‚œβŠ›α΅£ r_shift2 f2 = t `β‚œβŠ› a_shift2 (f1 βŠ›α΅£ f2)
Ξ“: t_ctx
a, b: ty0
t: term Ξ“ (a β†’ b)
H: forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (t `β‚œβŠ› f1) β‚œβŠ›α΅£ f2 = t `β‚œβŠ› f1 βŠ›α΅£ f2
Ο€: ev_ctx Ξ“ b
H0: forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (Ο€ `β‚‘βŠ› f1) β‚‘βŠ›α΅£ f2 = Ο€ `β‚‘βŠ› f1 βŠ›α΅£ f2
Ξ“2, Ξ“3: t_ctx
f1: Ξ“ =[ val_m ]> Ξ“2
f2: Ξ“2 βŠ† Ξ“3
(t `β‚œβŠ› f1) β‚œβŠ›α΅£ f2 = t `β‚œβŠ› f1 βŠ›α΅£ f2
Ξ“: t_ctx
a, b: ty0
t: term Ξ“ (a β†’ b)
H: forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (t `β‚œβŠ› f1) β‚œβŠ›α΅£ f2 = t `β‚œβŠ› f1 βŠ›α΅£ f2
Ο€: ev_ctx Ξ“ b
H0: forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (Ο€ `β‚‘βŠ› f1) β‚‘βŠ›α΅£ f2 = Ο€ `β‚‘βŠ› f1 βŠ›α΅£ f2
Ξ“2, Ξ“3: t_ctx
f1: Ξ“ =[ val_m ]> Ξ“2
f2: Ξ“2 βŠ† Ξ“3
(Ο€ `β‚‘βŠ› f1) β‚‘βŠ›α΅£ f2 = Ο€ `β‚‘βŠ› f1 βŠ›α΅£ f2
Ξ“: t_ctx
a, b: ty0
v: val Ξ“ a
H: forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (v `α΅₯βŠ› f1) α΅₯βŠ›α΅£ f2 = v `α΅₯βŠ› f1 βŠ›α΅£ f2
Ο€: ev_ctx Ξ“ b
H0: forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (Ο€ `β‚‘βŠ› f1) β‚‘βŠ›α΅£ f2 = Ο€ `β‚‘βŠ› f1 βŠ›α΅£ f2
Ξ“2, Ξ“3: t_ctx
f1: Ξ“ =[ val_m ]> Ξ“2
f2: Ξ“2 βŠ† Ξ“3
(v `α΅₯βŠ› f1) α΅₯βŠ›α΅£ f2 = v `α΅₯βŠ› f1 βŠ›α΅£ f2
Ξ“: t_ctx
a, b: ty0
v: val Ξ“ a
H: forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (v `α΅₯βŠ› f1) α΅₯βŠ›α΅£ f2 = v `α΅₯βŠ› f1 βŠ›α΅£ f2
Ο€: ev_ctx Ξ“ b
H0: forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 βŠ† Ξ“3), (Ο€ `β‚‘βŠ› f1) β‚‘βŠ›α΅£ f2 = Ο€ `β‚‘βŠ› f1 βŠ›α΅£ f2
Ξ“2, Ξ“3: t_ctx
f1: Ξ“ =[ val_m ]> Ξ“2
f2: Ξ“2 βŠ† Ξ“3
(Ο€ `β‚‘βŠ› f1) β‚‘βŠ›α΅£ f2 = Ο€ `β‚‘βŠ› f1 βŠ›α΅£ f2
all: try rewrite a_shift2_a_ren; auto. Qed.
Ξ“1, Ξ“2, Ξ“3: t_ctx
f1: Ξ“1 =[ val_m ]> Ξ“2
f2: Ξ“2 βŠ† Ξ“3
a: ty0
t: term Ξ“1 a

(t `β‚œβŠ› f1) β‚œβŠ›α΅£ f2 = t `β‚œβŠ› f1 βŠ›α΅£ f2
now apply (term_ind_mut _ _ _ ren_sub_prf). Qed.
Ξ“1, Ξ“2, Ξ“3: t_ctx
f1: Ξ“1 =[ val_m ]> Ξ“2
f2: Ξ“2 βŠ† Ξ“3
a: ty0
v: val Ξ“1 a

(v `α΅₯βŠ› f1) α΅₯βŠ›α΅£ f2 = v `α΅₯βŠ› f1 βŠ›α΅£ f2
now apply (val_ind_mut _ _ _ ren_sub_prf). Qed.
Ξ“1, Ξ“2, Ξ“3: t_ctx
f1: Ξ“1 =[ val_m ]> Ξ“2
f2: Ξ“2 βŠ† Ξ“3
a: ty0
Ο€: ev_ctx Ξ“1 a

(Ο€ `β‚‘βŠ› f1) β‚‘βŠ›α΅£ f2 = Ο€ `β‚‘βŠ› f1 βŠ›α΅£ f2
now apply (ctx_ind_mut _ _ _ ren_sub_prf). Qed.
Ξ“1, Ξ“2, Ξ“3: t_ctx
f1: Ξ“1 =[ val_m ]> Ξ“2
f2: Ξ“2 βŠ† Ξ“3
a: ty
v: val_m Ξ“1 a

(v α΅₯βŠ› f1) β‚˜βŠ›α΅£ f2 = v α΅₯βŠ› f1 βŠ›α΅£ f2
destruct a; [ now apply v_ren_sub | now apply e_ren_sub ]. Qed.
Ξ“1, Ξ“2, Ξ“3: t_ctx
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 a (t : term Ξ“1 a) : Prop :=
  forall Ξ“2 Ξ“3 (f1 : Ξ“1 βŠ† Ξ“2) (f2 : Ξ“2 =[val_m]> Ξ“3) ,
  (t β‚œβŠ›α΅£ f1) `β‚œβŠ› f2 = t `β‚œβŠ› (f1 α΅£βŠ› f2) .
Definition v_sub_ren_P Ξ“1 a (v : val Ξ“1 a) : Prop :=
  forall Ξ“2 Ξ“3 (f1 : Ξ“1 βŠ† Ξ“2) (f2 : Ξ“2 =[val_m]> Ξ“3) ,
  (v α΅₯βŠ›α΅£ f1) `α΅₯βŠ› f2 = v `α΅₯βŠ› (f1 α΅£βŠ› f2) .
Definition e_sub_ren_P Ξ“1 a (Ο€ : ev_ctx Ξ“1 a) : Prop :=
  forall Ξ“2 Ξ“3 (f1 : Ξ“1 βŠ† Ξ“2) (f2 : Ξ“2 =[val_m]> Ξ“3) ,
  (Ο€ β‚‘βŠ›α΅£ f1) `β‚‘βŠ› f2 = Ο€ `β‚‘βŠ› (f1 α΅£βŠ› f2) .

syn_ind_args t_sub_ren_P v_sub_ren_P e_sub_ren_P

forall (Ξ“ : t_ctx) (a : ty0) (v : val Ξ“ a), v_sub_ren_P Ξ“ a v -> t_sub_ren_P Ξ“ a v

forall (Ξ“ : t_ctx) (a b : ty0) (t1 : term Ξ“ (a β†’ b)), t_sub_ren_P Ξ“ (a β†’ b) t1 -> forall t2 : term Ξ“ a, t_sub_ren_P Ξ“ a t2 -> t_sub_ren_P Ξ“ b (App t1 t2)

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ + a), v_sub_ren_P Ξ“ a (Var i)

forall Ξ“ : t_ctx, v_sub_ren_P Ξ“ ΞΉ TT

forall (Ξ“ : t_ctx) (a b : ty0) (t : term (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b), t_sub_ren_P (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b t -> v_sub_ren_P Ξ“ (a β†’ b) (Lam t)

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ Β¬ a), e_sub_ren_P Ξ“ a (K0 i)

forall (Ξ“ : t_ctx) (a b : ty0) (t : term Ξ“ (a β†’ b)), t_sub_ren_P Ξ“ (a β†’ b) t -> forall Ο€ : ev_ctx Ξ“ b, e_sub_ren_P Ξ“ b Ο€ -> e_sub_ren_P Ξ“ a (K1 t Ο€)

forall (Ξ“ : t_ctx) (a b : ty0) (v : val Ξ“ a), v_sub_ren_P Ξ“ a v -> forall Ο€ : ev_ctx Ξ“ b, e_sub_ren_P Ξ“ b Ο€ -> e_sub_ren_P Ξ“ (a β†’ b) (K2 v Ο€)

forall (Ξ“ : t_ctx) (a : ty0) (v : val Ξ“ a), (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), v α΅₯βŠ›α΅£ f1 `α΅₯βŠ› f2 = v `α΅₯βŠ› f1 α΅£βŠ› f2) -> forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), v β‚œβŠ›α΅£ f1 `β‚œβŠ› f2 = v `β‚œβŠ› f1 α΅£βŠ› f2

forall (Ξ“ : t_ctx) (a b : ty0) (t1 : term Ξ“ (a β†’ b)), (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), t1 β‚œβŠ›α΅£ f1 `β‚œβŠ› f2 = t1 `β‚œβŠ› f1 α΅£βŠ› f2) -> forall t2 : term Ξ“ a, (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), t2 β‚œβŠ›α΅£ f1 `β‚œβŠ› f2 = t2 `β‚œβŠ› f1 α΅£βŠ› f2) -> forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), App t1 t2 β‚œβŠ›α΅£ f1 `β‚œβŠ› f2 = App t1 t2 `β‚œβŠ› f1 α΅£βŠ› f2

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ + a) (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), Var i α΅₯βŠ›α΅£ f1 `α΅₯βŠ› f2 = Var i `α΅₯βŠ› f1 α΅£βŠ› f2

forall (Ξ“ Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), TT α΅₯βŠ›α΅£ f1 `α΅₯βŠ› f2 = TT `α΅₯βŠ› f1 α΅£βŠ› f2

forall (Ξ“ : t_ctx) (a b : ty0) (t : term (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b), (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) βŠ† Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), t β‚œβŠ›α΅£ f1 `β‚œβŠ› f2 = t `β‚œβŠ› f1 α΅£βŠ› f2) -> forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), Lam t α΅₯βŠ›α΅£ f1 `α΅₯βŠ› f2 = Lam t `α΅₯βŠ› f1 α΅£βŠ› f2

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ Β¬ a) (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), K0 i β‚‘βŠ›α΅£ f1 `β‚‘βŠ› f2 = K0 i `β‚‘βŠ› f1 α΅£βŠ› f2

forall (Ξ“ : t_ctx) (a b : ty0) (t : term Ξ“ (a β†’ b)), (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), t β‚œβŠ›α΅£ f1 `β‚œβŠ› f2 = t `β‚œβŠ› f1 α΅£βŠ› f2) -> forall Ο€ : ev_ctx Ξ“ b, (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), Ο€ β‚‘βŠ›α΅£ f1 `β‚‘βŠ› f2 = Ο€ `β‚‘βŠ› f1 α΅£βŠ› f2) -> forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), K1 t Ο€ β‚‘βŠ›α΅£ f1 `β‚‘βŠ› f2 = K1 t Ο€ `β‚‘βŠ› f1 α΅£βŠ› f2

forall (Ξ“ : t_ctx) (a b : ty0) (v : val Ξ“ a), (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), v α΅₯βŠ›α΅£ f1 `α΅₯βŠ› f2 = v `α΅₯βŠ› f1 α΅£βŠ› f2) -> forall Ο€ : ev_ctx Ξ“ b, (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), Ο€ β‚‘βŠ›α΅£ f1 `β‚‘βŠ› f2 = Ο€ `β‚‘βŠ› f1 α΅£βŠ› f2) -> forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ βŠ† Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), K2 v Ο€ β‚‘βŠ›α΅£ f1 `β‚‘βŠ› f2 = K2 v Ο€ `β‚‘βŠ› f1 α΅£βŠ› f2
Ξ“: t_ctx
a, b: ty0
t: term (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b
H: forall (Ξ“2 Ξ“3 : t_ctx) (f1 : (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) βŠ† Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), t β‚œβŠ›α΅£ f1 `β‚œβŠ› f2 = t `β‚œβŠ› f1 α΅£βŠ› f2
Ξ“2, Ξ“3: t_ctx
f1: Ξ“ βŠ† Ξ“2
f2: Ξ“2 =[ val_m ]> Ξ“3

t β‚œβŠ›α΅£ r_shift2 f1 `β‚œβŠ› a_shift2 f2 = t `β‚œβŠ› a_shift2 (f1 α΅£βŠ› f2)
now rewrite a_shift2_s_ren. Qed.
Ξ“1, Ξ“2: ctx ty
Ξ“3: t_ctx
f1: Ξ“1 βŠ† Ξ“2
f2: Ξ“2 =[ val_m ]> Ξ“3
a: ty0
t: term Ξ“1 a

t β‚œβŠ›α΅£ f1 `β‚œβŠ› f2 = t `β‚œβŠ› f1 α΅£βŠ› f2
now apply (term_ind_mut _ _ _ sub_ren_prf). Qed.
Ξ“1, Ξ“2: ctx ty
Ξ“3: t_ctx
f1: Ξ“1 βŠ† Ξ“2
f2: Ξ“2 =[ val_m ]> Ξ“3
a: ty0
v: val Ξ“1 a

v α΅₯βŠ›α΅£ f1 `α΅₯βŠ› f2 = v `α΅₯βŠ› f1 α΅£βŠ› f2
now apply (val_ind_mut _ _ _ sub_ren_prf). Qed.
Ξ“1, Ξ“2: ctx ty
Ξ“3: t_ctx
f1: Ξ“1 βŠ† Ξ“2
f2: Ξ“2 =[ val_m ]> Ξ“3
a: ty0
Ο€: ev_ctx Ξ“1 a

Ο€ β‚‘βŠ›α΅£ f1 `β‚‘βŠ› f2 = Ο€ `β‚‘βŠ› f1 α΅£βŠ› f2
now apply (ctx_ind_mut _ _ _ sub_ren_prf). Qed.
Ξ“1, Ξ“2: ctx ty
Ξ“3: t_ctx
f1: Ξ“1 βŠ† Ξ“2
f2: Ξ“2 =[ val_m ]> Ξ“3
a: ty
v: val_m Ξ“1 a

v β‚˜βŠ›α΅£ f1 α΅₯βŠ› f2 = v α΅₯βŠ› f1 α΅£βŠ› f2
destruct a; [ now apply v_sub_ren | now apply e_sub_ren ]. Qed.
Ξ“1, Ξ“2: ctx ty
Ξ“3: t_ctx
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 Ξ“ a (t : term Ξ“ a) : Prop := t `β‚œβŠ› a_id = t .
Definition v_sub_id_l_P Ξ“ a (v : val Ξ“ a) : Prop := v `α΅₯βŠ› a_id = v .
Definition e_sub_id_l_P Ξ“ a (Ο€ : ev_ctx Ξ“ a) : Prop := Ο€ `β‚‘βŠ› a_id = Ο€ .

syn_ind_args t_sub_id_l_P v_sub_id_l_P e_sub_id_l_P

forall (Ξ“ : t_ctx) (a : ty0) (v : val Ξ“ a), v_sub_id_l_P Ξ“ a v -> t_sub_id_l_P Ξ“ a v

forall (Ξ“ : t_ctx) (a b : ty0) (t1 : term Ξ“ (a β†’ b)), t_sub_id_l_P Ξ“ (a β†’ b) t1 -> forall t2 : term Ξ“ a, t_sub_id_l_P Ξ“ a t2 -> t_sub_id_l_P Ξ“ b (App t1 t2)

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ + a), v_sub_id_l_P Ξ“ a (Var i)

forall Ξ“ : t_ctx, v_sub_id_l_P Ξ“ ΞΉ TT

forall (Ξ“ : t_ctx) (a b : ty0) (t : term (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b), t_sub_id_l_P (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b t -> v_sub_id_l_P Ξ“ (a β†’ b) (Lam t)

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ Β¬ a), e_sub_id_l_P Ξ“ a (K0 i)

forall (Ξ“ : t_ctx) (a b : ty0) (t : term Ξ“ (a β†’ b)), t_sub_id_l_P Ξ“ (a β†’ b) t -> forall Ο€ : ev_ctx Ξ“ b, e_sub_id_l_P Ξ“ b Ο€ -> e_sub_id_l_P Ξ“ a (K1 t Ο€)

forall (Ξ“ : t_ctx) (a b : ty0) (v : val Ξ“ a), v_sub_id_l_P Ξ“ a v -> forall Ο€ : ev_ctx Ξ“ b, e_sub_id_l_P Ξ“ b Ο€ -> e_sub_id_l_P Ξ“ (a β†’ b) (K2 v Ο€)

forall (Ξ“ : t_ctx) (a : ty0) (v : val Ξ“ a), v `α΅₯βŠ› a_id = v -> v `β‚œβŠ› a_id = v

forall (Ξ“ : t_ctx) (a b : ty0) (t1 : term Ξ“ (a β†’ b)), t1 `β‚œβŠ› a_id = t1 -> forall t2 : term Ξ“ a, t2 `β‚œβŠ› a_id = t2 -> App t1 t2 `β‚œβŠ› a_id = App t1 t2

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ + a), Var i `α΅₯βŠ› a_id = Var i

forall Ξ“ : t_ctx, TT `α΅₯βŠ› a_id = TT

forall (Ξ“ : t_ctx) (a b : ty0) (t : term (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b), t `β‚œβŠ› a_id = t -> Lam t `α΅₯βŠ› a_id = Lam t

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ Β¬ a), K0 i `β‚‘βŠ› a_id = K0 i

forall (Ξ“ : t_ctx) (a b : ty0) (t : term Ξ“ (a β†’ b)), t `β‚œβŠ› a_id = t -> forall Ο€ : ev_ctx Ξ“ b, Ο€ `β‚‘βŠ› a_id = Ο€ -> K1 t Ο€ `β‚‘βŠ› a_id = K1 t Ο€

forall (Ξ“ : t_ctx) (a b : ty0) (v : val Ξ“ a), v `α΅₯βŠ› a_id = v -> forall Ο€ : ev_ctx Ξ“ b, Ο€ `β‚‘βŠ› a_id = Ο€ -> K2 v Ο€ `β‚‘βŠ› a_id = K2 v Ο€
Ξ“: t_ctx
a, b: ty0
t: term (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b
H: t `β‚œβŠ› a_id = t

t `β‚œβŠ› a_shift2 a_id = t
now rewrite a_shift2_id. Qed.
Ξ“: t_ctx
a: ty0
t: term Ξ“ a

t `β‚œβŠ› a_id = t
now apply (term_ind_mut _ _ _ sub_id_l_prf). Qed.
Ξ“: t_ctx
a: ty0
v: val Ξ“ a

v `α΅₯βŠ› a_id = v
now apply (val_ind_mut _ _ _ sub_id_l_prf). Qed.
Ξ“: t_ctx
a: ty0
Ο€: ev_ctx Ξ“ a

Ο€ `β‚‘βŠ› a_id = Ο€
now apply (ctx_ind_mut _ _ _ sub_id_l_prf). Qed.
Ξ“: t_ctx
a: ty
v: val_m Ξ“ a

v α΅₯βŠ› a_id = v
destruct a; [ now apply v_sub_id_l | now apply e_sub_id_l ]. Qed.
Ξ“: t_ctx
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.

Ξ“1: ctx ty
Ξ“2: t_ctx
a: ty
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.

Ξ“1, Ξ“2, Ξ“3: t_ctx
a, b: ty
f1: Ξ“1 =[ val_m ]> Ξ“2
f2: Ξ“2 =[ val_m ]> Ξ“3

a_shift2 (f1 βŠ› f2) ≑ₐ a_shift2 f1 βŠ› a_shift2 f2
Ξ“1, Ξ“2, Ξ“3: t_ctx
a, b: ty
f1: Ξ“1 =[ val_m ]> Ξ“2
f2: Ξ“2 =[ val_m ]> Ξ“3
a0: ty
v: Ξ“1 β–Άβ‚“ a β–Άβ‚“ b βˆ‹ a0

a_shift2 (f1 βŠ› f2) a0 v = (a_shift2 f1 βŠ› a_shift2 f2)%asgn a0 v
Ξ“0: ctx ty
Ξ“2, Ξ“3: t_ctx
y0, y: ty
f1: Ξ“0 =[ val_m ]> Ξ“2
f2: Ξ“2 =[ val_m ]> Ξ“3
a0: ty
v: var a0 Ξ“0

a_shift2 (f1 βŠ› f2) a0 (pop (pop v)) = (a_shift2 f1 βŠ› a_shift2 f2)%asgn a0 (pop (pop v))
cbn; unfold m_shift2; now rewrite m_ren_sub, m_sub_ren. Qed.

Lemma 11: substitution-substitution associativity, ie composition law.

Definition t_sub_sub_P Ξ“1 a (t : term Ξ“1 a) : 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 a (v : val Ξ“1 a) : 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 a (Ο€ : ev_ctx Ξ“1 a) : Prop :=
  forall Ξ“2 Ξ“3 (f1 : Ξ“1 =[val_m]> Ξ“2) (f2 : Ξ“2 =[val_m]> Ξ“3) ,
  Ο€ `β‚‘βŠ› (f1 βŠ› f2) = (Ο€ `β‚‘βŠ› f1) `β‚‘βŠ› f2 .

syn_ind_args t_sub_sub_P v_sub_sub_P e_sub_sub_P

forall (Ξ“ : t_ctx) (a : ty0) (v : val Ξ“ a), v_sub_sub_P Ξ“ a v -> t_sub_sub_P Ξ“ a v

forall (Ξ“ : t_ctx) (a b : ty0) (t1 : term Ξ“ (a β†’ b)), t_sub_sub_P Ξ“ (a β†’ b) t1 -> forall t2 : term Ξ“ a, t_sub_sub_P Ξ“ a t2 -> t_sub_sub_P Ξ“ b (App t1 t2)

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ + a), v_sub_sub_P Ξ“ a (Var i)

forall Ξ“ : t_ctx, v_sub_sub_P Ξ“ ΞΉ TT

forall (Ξ“ : t_ctx) (a b : ty0) (t : term (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b), t_sub_sub_P (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b t -> v_sub_sub_P Ξ“ (a β†’ b) (Lam t)

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ Β¬ a), e_sub_sub_P Ξ“ a (K0 i)

forall (Ξ“ : t_ctx) (a b : ty0) (t : term Ξ“ (a β†’ b)), t_sub_sub_P Ξ“ (a β†’ b) t -> forall Ο€ : ev_ctx Ξ“ b, e_sub_sub_P Ξ“ b Ο€ -> e_sub_sub_P Ξ“ a (K1 t Ο€)

forall (Ξ“ : t_ctx) (a b : ty0) (v : val Ξ“ a), v_sub_sub_P Ξ“ a v -> forall Ο€ : ev_ctx Ξ“ b, e_sub_sub_P Ξ“ b Ο€ -> e_sub_sub_P Ξ“ (a β†’ b) (K2 v Ο€)

forall (Ξ“ : t_ctx) (a : ty0) (v : val Ξ“ a), (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), v `α΅₯βŠ› f1 βŠ› f2 = (v `α΅₯βŠ› f1) `α΅₯βŠ› f2) -> forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), v `β‚œβŠ› f1 βŠ› f2 = (v `β‚œβŠ› f1) `β‚œβŠ› f2

forall (Ξ“ : t_ctx) (a b : ty0) (t1 : term Ξ“ (a β†’ b)), (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), t1 `β‚œβŠ› f1 βŠ› f2 = (t1 `β‚œβŠ› f1) `β‚œβŠ› f2) -> forall t2 : term Ξ“ a, (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), t2 `β‚œβŠ› f1 βŠ› f2 = (t2 `β‚œβŠ› f1) `β‚œβŠ› f2) -> forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), App t1 t2 `β‚œβŠ› f1 βŠ› f2 = (App t1 t2 `β‚œβŠ› f1) `β‚œβŠ› f2

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ + a) (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), Var i `α΅₯βŠ› f1 βŠ› f2 = (Var i `α΅₯βŠ› f1) `α΅₯βŠ› f2

forall (Ξ“ Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), TT `α΅₯βŠ› f1 βŠ› f2 = (TT `α΅₯βŠ› f1) `α΅₯βŠ› f2

forall (Ξ“ : t_ctx) (a b : ty0) (t : term (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b), (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) =[ val_m ]> Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), t `β‚œβŠ› f1 βŠ› f2 = (t `β‚œβŠ› f1) `β‚œβŠ› f2) -> forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), Lam t `α΅₯βŠ› f1 βŠ› f2 = (Lam t `α΅₯βŠ› f1) `α΅₯βŠ› f2

forall (Ξ“ : t_ctx) (a : ty0) (i : Ξ“ βˆ‹ Β¬ a) (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), K0 i `β‚‘βŠ› f1 βŠ› f2 = (K0 i `β‚‘βŠ› f1) `β‚‘βŠ› f2

forall (Ξ“ : t_ctx) (a b : ty0) (t : term Ξ“ (a β†’ b)), (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), t `β‚œβŠ› f1 βŠ› f2 = (t `β‚œβŠ› f1) `β‚œβŠ› f2) -> forall Ο€ : ev_ctx Ξ“ b, (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), Ο€ `β‚‘βŠ› f1 βŠ› f2 = (Ο€ `β‚‘βŠ› f1) `β‚‘βŠ› f2) -> forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), K1 t Ο€ `β‚‘βŠ› f1 βŠ› f2 = (K1 t Ο€ `β‚‘βŠ› f1) `β‚‘βŠ› f2

forall (Ξ“ : t_ctx) (a b : ty0) (v : val Ξ“ a), (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), v `α΅₯βŠ› f1 βŠ› f2 = (v `α΅₯βŠ› f1) `α΅₯βŠ› f2) -> forall Ο€ : ev_ctx Ξ“ b, (forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), Ο€ `β‚‘βŠ› f1 βŠ› f2 = (Ο€ `β‚‘βŠ› f1) `β‚‘βŠ› f2) -> forall (Ξ“2 Ξ“3 : t_ctx) (f1 : Ξ“ =[ val_m ]> Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), K2 v Ο€ `β‚‘βŠ› f1 βŠ› f2 = (K2 v Ο€ `β‚‘βŠ› f1) `β‚‘βŠ› f2
Ξ“: t_ctx
a, b: ty0
t: term (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b
H: forall (Ξ“2 Ξ“3 : t_ctx) (f1 : (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) =[ val_m ]> Ξ“2) (f2 : Ξ“2 =[ val_m ]> Ξ“3), t `β‚œβŠ› f1 βŠ› f2 = (t `β‚œβŠ› f1) `β‚œβŠ› f2
Ξ“2, Ξ“3: t_ctx
f1: Ξ“ =[ val_m ]> Ξ“2
f2: Ξ“2 =[ val_m ]> Ξ“3

t `β‚œβŠ› a_shift2 (f1 βŠ› f2) = (t `β‚œβŠ› a_shift2 f1) `β‚œβŠ› a_shift2 f2
now rewrite a_shift2_comp. Qed.
Ξ“1, Ξ“2, Ξ“3: t_ctx
f1: Ξ“1 =[ val_m ]> Ξ“2
f2: Ξ“2 =[ val_m ]> Ξ“3
a: ty0
t: term Ξ“1 a

t `β‚œβŠ› f1 βŠ› f2 = (t `β‚œβŠ› f1) `β‚œβŠ› f2
now apply (term_ind_mut _ _ _ sub_sub_prf). Qed.
Ξ“1, Ξ“2, Ξ“3: t_ctx
f1: Ξ“1 =[ val_m ]> Ξ“2
f2: Ξ“2 =[ val_m ]> Ξ“3
a: ty0
v: val Ξ“1 a

v `α΅₯βŠ› f1 βŠ› f2 = (v `α΅₯βŠ› f1) `α΅₯βŠ› f2
now apply (val_ind_mut _ _ _ sub_sub_prf). Qed.
Ξ“1, Ξ“2, Ξ“3: t_ctx
f1: Ξ“1 =[ val_m ]> Ξ“2
f2: Ξ“2 =[ val_m ]> Ξ“3
a: ty0
Ο€: ev_ctx Ξ“1 a

Ο€ `β‚‘βŠ› f1 βŠ› f2 = (Ο€ `β‚‘βŠ› f1) `β‚‘βŠ› f2
now apply (ctx_ind_mut _ _ _ sub_sub_prf). Qed.
Ξ“1, Ξ“2, Ξ“3: t_ctx
a: ty
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.
Ξ“1, Ξ“2, Ξ“3: t_ctx
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.
Ξ“, Ξ”: t_ctx
a, b: ty
f: Ξ“ =[ val_m ]> Ξ”
v1: val_m Ξ“ a
v2: val_m Ξ“ b

a_shift2 f βŠ› assign2 (v1 α΅₯βŠ› f) (v2 α΅₯βŠ› f) ≑ₐ assign2 v1 v2 βŠ› f
Ξ“, Ξ”: t_ctx
a, b: ty
f: Ξ“ =[ val_m ]> Ξ”
v1: val_m Ξ“ a
v2: val_m Ξ“ b
a0: ty
v: Ξ“ β–Άβ‚“ a β–Άβ‚“ b βˆ‹ a0

(a_shift2 f βŠ› assign2 (v1 α΅₯βŠ› f) (v2 α΅₯βŠ› f))%asgn a0 v = (assign2 v1 v2 βŠ› f)%asgn a0 v
Ξ“1: ctx ty
Ξ”: t_ctx
y0, y: ty
f: Ξ“1 =[ val_m ]> Ξ”
v1: val_m Ξ“1 y0
v2: val_m Ξ“1 y
a0: ty
v: var a0 Ξ“1

(a_shift2 f βŠ› assign2 (v1 α΅₯βŠ› f) (v2 α΅₯βŠ› f))%asgn a0 (pop (pop v)) = (assign2 v1 v2 βŠ› f)%asgn a0 (pop (pop v))
Ξ“1: ctx ty
Ξ”: t_ctx
y0, y: ty
f: Ξ“1 =[ val_m ]> Ξ”
v1: val_m Ξ“1 y0
v2: val_m Ξ“1 y
a0: ty
v: var a0 Ξ“1

f a0 v α΅₯βŠ› (r_pop α΅£βŠ› r_pop) α΅£βŠ› assign2 (m_subst Ξ“1 y0 v1 Ξ” f) (m_subst Ξ“1 y v2 Ξ” f) = f a0 v
now apply m_sub_id_l. Qed.
Ξ“, Ξ”: t_ctx
x, y: ty
z: ty0
f: Ξ“ =[ val_m ]> Ξ”
t: term (Ξ“ β–Άβ‚“ x β–Άβ‚“ y) z
v1: val_m Ξ“ x
v2: val_m Ξ“ y

t `β‚œβŠ› a_shift2 f /[ v1 α΅₯βŠ› f, v2 α΅₯βŠ› f] = (t /[ v1, v2]) `β‚œβŠ› f
unfold t_subst2; now rewrite <- 2 t_sub_sub, a_sub2_sub. Qed.

The Actual Instance

Having proved all the basic syntactic properties of STLC, we are now ready to instanciate our framework!

As we only have negative types, we instanciate the interaction specification with types and observations. Beware that in more involved cases, the notion of "types" we give to the OGS construction does not coincide with the "language types": you should only give the negative types, or more intuitively, "non-shareable" types.

As hinted at the beginning, we instanciate the abstract value notion with our "machine values". They form a suitable monoid, which means we get a category of assigments, for which we now provide the proof of the laws.

#[global] Instance stlc_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 |} .

Configurations are instanciated with our states, and what we have proved earlier amounts to showing they are a right-module on values.

#[global] Instance stlc_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 |} .

In our generic theorem, there is a finicky lemma that is the counter-part to the exclusion of any "infinite chit-chat" that one finds in other accounts of OGS and other game semantics. The way we have proved it requires a little bit more structure on values. Specifically, we need to show that a_id is injective and that its fibers are decidable and invert renamings. These technicalities are easily shown by induction on values but help us to distinguish conveniently between values which are variables and others.


var_assumptions val_m

forall (Ξ“ : t_ctx) (x : ty), injective (Subst.a_id (x:=x))

forall (Ξ“ : t_ctx) (x : ty) (v : val_m Ξ“ x), decidable (is_var v)

forall (Ξ“1 Ξ“2 : t_ctx) (x : ty) (v : val_m Ξ“1 x) (r : Ξ“1 βŠ† Ξ“2), is_var (v_ren v r) -> is_var v

forall (Ξ“ : t_ctx) (x : ty), injective (Subst.a_id (x:=x))
intros ? [] ?? H; now dependent destruction H.

forall (Ξ“ : t_ctx) (x : ty) (v : val_m Ξ“ x), decidable (is_var v)
Ξ“: t_ctx
t: ty0

decidable (is_var TT)
Ξ“: t_ctx
t, a, b: ty0
t0: term (Ξ“ β–Άβ‚“ + (a β†’ b) β–Άβ‚“ + a) b
decidable (is_var (Lam t0))
Ξ“: t_ctx
t, a, b: ty0
t0: term Ξ“ (a β†’ b)
e: ev_ctx Ξ“ b
decidable (is_var (K1 t0 e))
Ξ“: t_ctx
t, a, b: ty0
v: val Ξ“ a
e: ev_ctx Ξ“ b
decidable (is_var (K2 v e))
all: apply No; intro H; dependent destruction H.

forall (Ξ“1 Ξ“2 : t_ctx) (x : ty) (v : val_m Ξ“1 x) (r : Ξ“1 βŠ† Ξ“2), is_var (v_ren v r) -> is_var v
intros ?? [] v r H; induction v; dependent destruction H; exact (Vvar _). Qed.

We now instanciate the machine with stlc_eval as the active step ("compute the next observable action") and obs_app as the passive step ("resume from a stuck state").

#[global] Instance stlc_machine : machine val_m state obs_op :=
  {| eval := @stlc_eval ;
     oapp := @obs_app |} .

All that is left is to prove our theorem-specific hypotheses. All but another technical lemma for the chit-chat problem are again coherence conditions between eval and app and the monoidal structure of values and configurations.


machine_laws val_m state obs_op
Ξ“: t_ctx
x: ty
v: val_m Ξ“ x
o: obs x

Proper (asgn_eq (obs_dom o) Ξ“ ==> eq) (obs_app v o)
Ξ“1, Ξ“2: t_ctx
x: ty
v: val_m Ξ“1 x
o: obs x
a: obs_dom o =[ val_m ]> Ξ“1
b: Ξ“1 =[ val_m ]> Ξ“2
m_var (obs_app v o a) `β‚œβŠ› b β‹… m_obs (obs_app v o a) `β‚‘βŠ› b = obs_app (m_subst Ξ“1 x v Ξ“2 b) o (a βŠ› b)
Ξ“, Ξ”: t_ctx
c: state Ξ“
a: Ξ“ =[ val_m ]> Ξ”
stlc_eval (m_var c `β‚œβŠ› a β‹… m_obs c `β‚‘βŠ› a) ≋ bind_delay' (stlc_eval c) (fun n : Obs.nf obs_op val_m Ξ“ => stlc_eval (m_var (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚œβŠ› a β‹… m_obs (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚‘βŠ› a))
Ξ“: t_ctx
u: Obs.nf obs_op val_m Ξ“
stlc_eval (obs_app (a_id (nf_ty u) (nf_var u)) (nf_obs u) (nf_args u)) ≋ ret_delay u

well_founded head_inst_nostep

The first one proves that obs_app respects pointwise equality of assigments.

  
Ξ“: t_ctx
x: ty
v: val_m Ξ“ x
o: obs x

Proper (asgn_eq (obs_dom o) Ξ“ ==> eq) (obs_app v o)
intros ?? H1; dependent elimination o; cbn; repeat (f_equal; auto).

The second one proves a commutation law of obs_app with renamings.

  
Ξ“1, Ξ“2: t_ctx
x: ty
v: val_m Ξ“1 x
o: obs x
a: obs_dom o =[ val_m ]> Ξ“1
b: Ξ“1 =[ val_m ]> Ξ“2

m_var (obs_app v o a) `β‚œβŠ› b β‹… m_obs (obs_app v o a) `β‚‘βŠ› b = obs_app (m_subst Ξ“1 x v Ξ“2 b) o (a βŠ› b)
destruct x; dependent elimination o; cbn; f_equal.

The meat of our abstract proof is this next one. We need to prove that our evaluator respects substitution in a suitable sense: evaluating a substituted configuration must be the same thing as evaluating the configuration, then "substituting" the normal form and continuing the evaluation.

While potentially scary, the proof is direct and this actually amount to checking that indeed, when unrolling our evaluator, this is what happens.

  
Ξ“, Ξ”: t_ctx
c: state Ξ“
a: Ξ“ =[ val_m ]> Ξ”

stlc_eval (m_var c `β‚œβŠ› a β‹… m_obs c `β‚‘βŠ› a) ≋ bind_delay' (stlc_eval c) (fun n : Obs.nf obs_op val_m Ξ“ => stlc_eval (m_var (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚œβŠ› a β‹… m_obs (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚‘βŠ› a))
Ξ“, Ξ”: t_ctx
R: relα΅’ (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”)) (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”))
CIH: forall (c : state Ξ“) (a : Ξ“ =[ val_m ]> Ξ”), it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R T1_0 (stlc_eval (m_var c `β‚œβŠ› a β‹… m_obs c `β‚‘βŠ› a)) (bind_delay' (stlc_eval c) (fun n : Obs.nf obs_op val_m Ξ“ => stlc_eval (m_var (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚œβŠ› a β‹… m_obs (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚‘βŠ› a)))
c: state Ξ“
e: Ξ“ =[ val_m ]> Ξ”

it_eq_bt Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R T1_0 (stlc_eval (m_var c `β‚œβŠ› e β‹… m_obs c `β‚‘βŠ› e)) (bind_delay' (stlc_eval c) (fun n : Obs.nf obs_op val_m Ξ“ => stlc_eval (m_var (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚œβŠ› e β‹… m_obs (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚‘βŠ› e)))
Ξ“: t_ctx
a0: ty0
v: val Ξ“ a0
i: Ξ“ βˆ‹ Β¬ a0
Ξ”: t_ctx
R: relα΅’ (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”)) (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”))
CIH: forall (c : state Ξ“) (a : Ξ“ =[ val_m ]> Ξ”), it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R T1_0 (stlc_eval (m_var c `β‚œβŠ› a β‹… m_obs c `β‚‘βŠ› a)) (bind_delay' (stlc_eval c) (fun n : Obs.nf obs_op val_m Ξ“ => stlc_eval (m_var (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚œβŠ› a β‹… m_obs (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚‘βŠ› a)))
e: Ξ“ =[ val_m ]> Ξ”
H: {| pr1 := ORet; pr2 := (! ▢ₐ v)%asgn |} = {| pr1 := ORet; pr2 := (! ▢ₐ v)%asgn |}

it_eqF Event.emptyβ‚‘ (fun _ : T1 => nf_eq) (it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R) T1_0 match match e Β¬ a0 i in (ev_ctx _ t) return (val Ξ” t -> state Ξ” + nf Ξ”) with | @K0 _ a c => fun v : val Ξ” a => inr (c β‹… ORet ⦇ ! ▢ₐ v ⦈) | @K1 _ a b t e => fun v : val Ξ” a => inl (t β‹… K2 v e) | @K2 _ a b v e => fun v0 : val Ξ” (a β†’ b) => match v0 in (val _ t) return (val Ξ” a -> ev_ctx Ξ” b -> t = a β†’ b -> state Ξ” + nf Ξ”) with | @Var _ a0 c => fun (v1 : val Ξ” a) (e0 : ev_ctx Ξ” b) (H : a0 = a β†’ b) => DepElim.solution_left (a β†’ b) (fun (c0 : cvar Ξ” + (a β†’ b)) (v2 : val Ξ” a) (e1 : ev_ctx Ξ” b) => inr (c0 β‹… OApp ⦇ (! ▢ₐ v2) ▢ₐ e1 ⦈)) a0 H c v1 e0 | TT => fun (_ : val Ξ” a) (_ : ev_ctx Ξ” b) => apply_noConfusion ΞΉ (a β†’ b) (False_rect (state Ξ” + nf Ξ”)) | @Lam _ a0 b0 t => fun (v1 : val Ξ” a) (e0 : ev_ctx Ξ” b) => apply_noConfusion (a0 β†’ b0) (a β†’ b) (DepElim.eq_simplification_sigma1 a0 a b0 b (fun H : a0 = a => DepElim.solution_left a (fun (b1 : ty0) (t0 : term (Ξ” β–Άβ‚“ ... β–Άβ‚“ + a) b1) (v2 : val Ξ” a) (e1 : ev_ctx Ξ” b) (H0 : b1 = b) => DepElim.solution_left b (fun (t1 : term ... b) (v3 : val Ξ” a) => inl ∘ Cut (...)) b1 H0 t0 v2 e1) a0 H b0 t v1 e0)) end v e eq_refl end (v `α΅₯βŠ› e) with | inl x => TauF (Structure.iter (fun pat : T1 => let 'T1_0 as x0 := pat return (state Ξ” -> itree Event.emptyβ‚‘ ((fun _ : T1 => state Ξ”) +α΅’ (fun _ : T1 => nf Ξ”)) x0) in ret_delay ∘ eval_step) T1_0 x) | inr y => RetF y end match match e Β¬ a0 i in (ev_ctx _ t) return (val Ξ” t -> state Ξ” + nf Ξ”) with | @K0 _ a c => fun v : val Ξ” a => inr (c β‹… ORet ⦇ ! ▢ₐ v ⦈) | @K1 _ a b t e => fun v : val Ξ” a => inl (t β‹… K2 v e) | @K2 _ a b v e => fun v0 : val Ξ” (a β†’ b) => match v0 in (val _ t) return (val Ξ” a -> ev_ctx Ξ” b -> t = a β†’ b -> state Ξ” + nf Ξ”) with | @Var _ a0 c => fun (v1 : val Ξ” a) (e0 : ev_ctx Ξ” b) (H : a0 = a β†’ b) => DepElim.solution_left (a β†’ b) (fun (c0 : cvar Ξ” + (a β†’ b)) (v2 : val Ξ” a) (e1 : ev_ctx Ξ” b) => inr (c0 β‹… OApp ⦇ (! ▢ₐ v2) ▢ₐ e1 ⦈)) a0 H c v1 e0 | TT => fun (_ : val Ξ” a) (_ : ev_ctx Ξ” b) => apply_noConfusion ΞΉ (a β†’ b) (False_rect (state Ξ” + nf Ξ”)) | @Lam _ a0 b0 t => fun (v1 : val Ξ” a) (e0 : ev_ctx Ξ” b) => apply_noConfusion (a0 β†’ b0) (a β†’ b) (DepElim.eq_simplification_sigma1 a0 a b0 b (fun H : a0 = a => DepElim.solution_left a (fun (b1 : ty0) (t0 : term (Ξ” β–Άβ‚“ ... β–Άβ‚“ + a) b1) (v2 : val Ξ” a) (e1 : ev_ctx Ξ” b) (H0 : b1 = b) => DepElim.solution_left b (fun (t1 : term ... b) (v3 : val Ξ” a) => inl ∘ Cut (...)) b1 H0 t0 v2 e1) a0 H b0 t v1 e0)) end v e eq_refl end (v `α΅₯βŠ› e) with | inl x => TauF (Structure.iter (fun pat : T1 => let 'T1_0 as x0 := pat return (state Ξ” -> itree Event.emptyβ‚‘ ((fun _ : T1 => state Ξ”) +α΅’ (fun _ : T1 => nf Ξ”)) x0) in ret_delay ∘ eval_step) T1_0 x) | inr y => RetF y end
Ξ“: t_ctx
a1: ty0
v: val Ξ“ a1
b: ty0
t: term Ξ“ (a1 β†’ b)
Ο€: ev_ctx Ξ“ b
Ξ”: t_ctx
R: relα΅’ (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”)) (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”))
CIH: forall (c : state Ξ“) (a : Ξ“ =[ val_m ]> Ξ”), it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R T1_0 (stlc_eval (m_var c `β‚œβŠ› a β‹… m_obs c `β‚‘βŠ› a)) (bind_delay' (stlc_eval c) (fun n : Obs.nf obs_op val_m Ξ“ => stlc_eval (m_var (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚œβŠ› a β‹… m_obs (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚‘βŠ› a)))
e: Ξ“ =[ val_m ]> Ξ”
H: K2 v Ο€ = K2 v Ο€
it_eqF Event.emptyβ‚‘ (fun _ : T1 => nf_eq) (it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R) T1_0 (TauF (Structure.iter (fun pat : T1 => let 'T1_0 as x := pat return (state Ξ” -> itree Event.emptyβ‚‘ ((fun _ : T1 => state Ξ”) +α΅’ (fun _ : T1 => nf Ξ”)) x) in ret_delay ∘ eval_step) T1_0 (t `β‚œβŠ› e β‹… K2 (v `α΅₯βŠ› e) (Ο€ `β‚‘βŠ› e)))) (TauF (Structure.subst (fun pat : T1 => let 'T1_0 as x := pat return (Obs.nf obs_op val_m Ξ“ -> itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”) x) in fun y : Obs.nf obs_op val_m Ξ“ => stlc_eval (m_var (obs_app (a_id (nf_ty y) (nf_var y)) (nf_obs y) (nf_args y)) `β‚œβŠ› e β‹… m_obs (obs_app (a_id (nf_ty y) (nf_var y)) (nf_obs y) (nf_args y)) `β‚‘βŠ› e)) T1_0 (Structure.iter (fun pat : T1 => let 'T1_0 as x := pat return (state Ξ“ -> itree Event.emptyβ‚‘ ((fun _ : T1 => state Ξ“) +α΅’ (fun _ : T1 => nf Ξ“)) x) in ret_delay ∘ eval_step) T1_0 (t β‹… K2 v Ο€))))
Ξ“: t_ctx
a2, b0: ty0
i: Ξ“ βˆ‹ + (a2 β†’ b0)
v: val Ξ“ a2
Ο€: ev_ctx Ξ“ b0
Ξ”: t_ctx
R: relα΅’ (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”)) (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”))
CIH: forall (c : state Ξ“) (a : Ξ“ =[ val_m ]> Ξ”), it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R T1_0 (stlc_eval (m_var c `β‚œβŠ› a β‹… m_obs c `β‚‘βŠ› a)) (bind_delay' (stlc_eval c) (fun n : Obs.nf obs_op val_m Ξ“ => stlc_eval (m_var (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚œβŠ› a β‹… m_obs (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚‘βŠ› a)))
e: Ξ“ =[ val_m ]> Ξ”
H: {| pr1 := OApp; pr2 := ((! ▢ₐ v) ▢ₐ Ο€)%asgn |} = {| pr1 := OApp; pr2 := ((! ▢ₐ v) ▢ₐ Ο€)%asgn |}
it_eqF Event.emptyβ‚‘ (fun _ : T1 => nf_eq) (it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R) T1_0 match match e + (a2 β†’ b0) i in (val _ t) return (val Ξ” a2 -> ev_ctx Ξ” b0 -> t = a2 β†’ b0 -> state Ξ” + nf Ξ”) with | @Var _ a c => fun (v0 : val Ξ” a2) (e0 : ev_ctx Ξ” b0) (H : a = a2 β†’ b0) => DepElim.solution_left (a2 β†’ b0) (fun (c0 : cvar Ξ” + (a2 β†’ b0)) (v1 : val Ξ” a2) (e1 : ev_ctx Ξ” b0) => inr (c0 β‹… OApp ⦇ (! ▢ₐ v1) ▢ₐ e1 ⦈)) a H c v0 e0 | TT => fun (_ : val Ξ” a2) (_ : ev_ctx Ξ” b0) => apply_noConfusion ΞΉ (a2 β†’ b0) (False_rect (state Ξ” + nf Ξ”)) | @Lam _ a b t => fun (v0 : val Ξ” a2) (e0 : ev_ctx Ξ” b0) => apply_noConfusion (a β†’ b) (a2 β†’ b0) (DepElim.eq_simplification_sigma1 a a2 b b0 (fun H : a = a2 => DepElim.solution_left a2 (fun (b1 : ty0) (t0 : term (Ξ” β–Άβ‚“ + (a2 β†’ b1) β–Άβ‚“ + a2) b1) (v1 : val Ξ” a2) (e1 : ev_ctx Ξ” b0) (H0 : b1 = b0) => DepElim.solution_left b0 (fun (t1 : term (Ξ” β–Άβ‚“ + ... β–Άβ‚“ + a2) b0) (v2 : val Ξ” a2) => inl ∘ Cut (t1 /[ Lam t1, v2])) b1 H0 t0 v1 e1) a H b t v0 e0)) end (v `α΅₯βŠ› e) (Ο€ `β‚‘βŠ› e) eq_refl with | inl x => TauF (Structure.iter (fun pat : T1 => let 'T1_0 as x0 := pat return (state Ξ” -> itree Event.emptyβ‚‘ ((fun _ : T1 => state Ξ”) +α΅’ (fun _ : T1 => nf Ξ”)) x0) in ret_delay ∘ eval_step) T1_0 x) | inr y => RetF y end match match e + (a2 β†’ b0) i in (val _ t) return (val Ξ” a2 -> ev_ctx Ξ” b0 -> t = a2 β†’ b0 -> state Ξ” + nf Ξ”) with | @Var _ a c => fun (v0 : val Ξ” a2) (e0 : ev_ctx Ξ” b0) (H : a = a2 β†’ b0) => DepElim.solution_left (a2 β†’ b0) (fun (c0 : cvar Ξ” + (a2 β†’ b0)) (v1 : val Ξ” a2) (e1 : ev_ctx Ξ” b0) => inr (c0 β‹… OApp ⦇ (! ▢ₐ v1) ▢ₐ e1 ⦈)) a H c v0 e0 | TT => fun (_ : val Ξ” a2) (_ : ev_ctx Ξ” b0) => apply_noConfusion ΞΉ (a2 β†’ b0) (False_rect (state Ξ” + nf Ξ”)) | @Lam _ a b t => fun (v0 : val Ξ” a2) (e0 : ev_ctx Ξ” b0) => apply_noConfusion (a β†’ b) (a2 β†’ b0) (DepElim.eq_simplification_sigma1 a a2 b b0 (fun H : a = a2 => DepElim.solution_left a2 (fun (b1 : ty0) (t0 : term (Ξ” β–Άβ‚“ + (a2 β†’ b1) β–Άβ‚“ + a2) b1) (v1 : val Ξ” a2) (e1 : ev_ctx Ξ” b0) (H0 : b1 = b0) => DepElim.solution_left b0 (fun (t1 : term (Ξ” β–Άβ‚“ + ... β–Άβ‚“ + a2) b0) (v2 : val Ξ” a2) => inl ∘ Cut (t1 /[ Lam t1, v2])) b1 H0 t0 v1 e1) a H b t v0 e0)) end (v `α΅₯βŠ› e) (Ο€ `β‚‘βŠ› e) eq_refl with | inl x => TauF (Structure.iter (fun pat : T1 => let 'T1_0 as x0 := pat return (state Ξ” -> itree Event.emptyβ‚‘ ((fun _ : T1 => state Ξ”) +α΅’ (fun _ : T1 => nf Ξ”)) x0) in ret_delay ∘ eval_step) T1_0 x) | inr y => RetF y end
Ξ“: t_ctx
a0, b: ty0
t: term (Ξ“ β–Άβ‚“ + (a0 β†’ b) β–Άβ‚“ + a0) b
v: val Ξ“ a0
Ο€: ev_ctx Ξ“ b
Ξ”: t_ctx
R: relα΅’ (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”)) (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”))
CIH: forall (c : state Ξ“) (a : Ξ“ =[ val_m ]> Ξ”), it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R T1_0 (stlc_eval (m_var c `β‚œβŠ› a β‹… m_obs c `β‚‘βŠ› a)) (bind_delay' (stlc_eval c) (fun n : Obs.nf obs_op val_m Ξ“ => stlc_eval (m_var (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚œβŠ› a β‹… m_obs (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚‘βŠ› a)))
e: Ξ“ =[ val_m ]> Ξ”
it_eqF Event.emptyβ‚‘ (fun _ : T1 => nf_eq) (it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R) T1_0 (TauF (Structure.iter (fun pat : T1 => let 'T1_0 as x := pat return (state Ξ” -> itree Event.emptyβ‚‘ ((fun _ : T1 => state Ξ”) +α΅’ (fun _ : T1 => nf Ξ”)) x) in ret_delay ∘ eval_step) T1_0 ((t `β‚œβŠ› a_shift2 e /[ Lam (t `β‚œβŠ› a_shift2 e), v `α΅₯βŠ› e]) β‹… Ο€ `β‚‘βŠ› e))) (TauF (Structure.subst (fun pat : T1 => let 'T1_0 as x := pat return (Obs.nf obs_op val_m Ξ“ -> itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”) x) in fun y : Obs.nf obs_op val_m Ξ“ => stlc_eval (m_var (obs_app (a_id (nf_ty y) (nf_var y)) (nf_obs y) (nf_args y)) `β‚œβŠ› e β‹… m_obs (obs_app (a_id (nf_ty y) (nf_var y)) (nf_obs y) (nf_args y)) `β‚‘βŠ› e)) T1_0 (Structure.iter (fun pat : T1 => let 'T1_0 as x := pat return (state Ξ“ -> itree Event.emptyβ‚‘ ((fun _ : T1 => state Ξ“) +α΅’ (fun _ : T1 => nf Ξ“)) x) in ret_delay ∘ eval_step) T1_0 ((t /[ Lam t, v]) β‹… Ο€))))
Ξ“: t_ctx
b, a0: ty0
t1: term Ξ“ (a0 β†’ b)
t2: term Ξ“ a0
Ο€: ev_ctx Ξ“ b
Ξ”: t_ctx
R: relα΅’ (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”)) (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”))
CIH: forall (c : state Ξ“) (a : Ξ“ =[ val_m ]> Ξ”), it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R T1_0 (stlc_eval (m_var c `β‚œβŠ› a β‹… m_obs c `β‚‘βŠ› a)) (bind_delay' (stlc_eval c) (fun n : Obs.nf obs_op val_m Ξ“ => stlc_eval (m_var (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚œβŠ› a β‹… m_obs (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚‘βŠ› a)))
e: Ξ“ =[ val_m ]> Ξ”
it_eqF Event.emptyβ‚‘ (fun _ : T1 => nf_eq) (it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R) T1_0 (TauF (Structure.iter (fun pat : T1 => let 'T1_0 as x := pat return (state Ξ” -> itree Event.emptyβ‚‘ ((fun _ : T1 => state Ξ”) +α΅’ (fun _ : T1 => nf Ξ”)) x) in ret_delay ∘ eval_step) T1_0 (t2 `β‚œβŠ› e β‹… K1 (t1 `β‚œβŠ› e) (Ο€ `β‚‘βŠ› e)))) (TauF (Structure.subst (fun pat : T1 => let 'T1_0 as x := pat return (Obs.nf obs_op val_m Ξ“ -> itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”) x) in fun y : Obs.nf obs_op val_m Ξ“ => stlc_eval (m_var (obs_app (a_id (nf_ty y) (nf_var y)) (nf_obs y) (nf_args y)) `β‚œβŠ› e β‹… m_obs (obs_app (a_id (nf_ty y) (nf_var y)) (nf_obs y) (nf_args y)) `β‚‘βŠ› e)) T1_0 (Structure.iter (fun pat : T1 => let 'T1_0 as x := pat return (state Ξ“ -> itree Event.emptyβ‚‘ ((fun _ : T1 => state Ξ“) +α΅’ (fun _ : T1 => nf Ξ“)) x) in ret_delay ∘ eval_step) T1_0 (t2 β‹… K1 t1 Ο€))))
Ξ“: t_ctx
a0: ty0
v: val Ξ“ a0
i: Ξ“ βˆ‹ Β¬ a0
Ξ”: t_ctx
R: relα΅’ (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”)) (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”))
CIH: forall (c : state Ξ“) (a : Ξ“ =[ val_m ]> Ξ”), it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R T1_0 (stlc_eval (m_var c `β‚œβŠ› a β‹… m_obs c `β‚‘βŠ› a)) (bind_delay' (stlc_eval c) (fun n : Obs.nf obs_op val_m Ξ“ => stlc_eval (m_var (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚œβŠ› a β‹… m_obs (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚‘βŠ› a)))
e: Ξ“ =[ val_m ]> Ξ”
H: {| pr1 := ORet; pr2 := (! ▢ₐ v)%asgn |} = {| pr1 := ORet; pr2 := (! ▢ₐ v)%asgn |}

it_eqF Event.emptyβ‚‘ (fun _ : T1 => nf_eq) (it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R) T1_0 match match e Β¬ a0 i in (ev_ctx _ t) return (val Ξ” t -> state Ξ” + nf Ξ”) with | @K0 _ a c => fun v : val Ξ” a => inr (c β‹… ORet ⦇ ! ▢ₐ v ⦈) | @K1 _ a b t e => fun v : val Ξ” a => inl (t β‹… K2 v e) | @K2 _ a b v e => fun v0 : val Ξ” (a β†’ b) => match v0 in (val _ t) return (val Ξ” a -> ev_ctx Ξ” b -> t = a β†’ b -> state Ξ” + nf Ξ”) with | @Var _ a0 c => fun (v1 : val Ξ” a) (e0 : ev_ctx Ξ” b) (H : a0 = a β†’ b) => DepElim.solution_left (a β†’ b) (fun (c0 : cvar Ξ” + (a β†’ b)) (v2 : val Ξ” a) (e1 : ev_ctx Ξ” b) => inr (c0 β‹… OApp ⦇ (! ▢ₐ v2) ▢ₐ e1 ⦈)) a0 H c v1 e0 | TT => fun (_ : val Ξ” a) (_ : ev_ctx Ξ” b) => apply_noConfusion ΞΉ (a β†’ b) (False_rect (state Ξ” + nf Ξ”)) | @Lam _ a0 b0 t => fun (v1 : val Ξ” a) (e0 : ev_ctx Ξ” b) => apply_noConfusion (a0 β†’ b0) (a β†’ b) (DepElim.eq_simplification_sigma1 a0 a b0 b (fun H : a0 = a => DepElim.solution_left a (fun (b1 : ty0) (t0 : term (Ξ” β–Άβ‚“ ... β–Άβ‚“ + a) b1) (v2 : val Ξ” a) (e1 : ev_ctx Ξ” b) (H0 : b1 = b) => DepElim.solution_left b (fun (t1 : term ... b) (v3 : val Ξ” a) => inl ∘ Cut (...)) b1 H0 t0 v2 e1) a0 H b0 t v1 e0)) end v e eq_refl end (v `α΅₯βŠ› e) with | inl x => TauF (Structure.iter (fun pat : T1 => let 'T1_0 as x0 := pat return (state Ξ” -> itree Event.emptyβ‚‘ ((fun _ : T1 => state Ξ”) +α΅’ (fun _ : T1 => nf Ξ”)) x0) in ret_delay ∘ eval_step) T1_0 x) | inr y => RetF y end match match e Β¬ a0 i in (ev_ctx _ t) return (val Ξ” t -> state Ξ” + nf Ξ”) with | @K0 _ a c => fun v : val Ξ” a => inr (c β‹… ORet ⦇ ! ▢ₐ v ⦈) | @K1 _ a b t e => fun v : val Ξ” a => inl (t β‹… K2 v e) | @K2 _ a b v e => fun v0 : val Ξ” (a β†’ b) => match v0 in (val _ t) return (val Ξ” a -> ev_ctx Ξ” b -> t = a β†’ b -> state Ξ” + nf Ξ”) with | @Var _ a0 c => fun (v1 : val Ξ” a) (e0 : ev_ctx Ξ” b) (H : a0 = a β†’ b) => DepElim.solution_left (a β†’ b) (fun (c0 : cvar Ξ” + (a β†’ b)) (v2 : val Ξ” a) (e1 : ev_ctx Ξ” b) => inr (c0 β‹… OApp ⦇ (! ▢ₐ v2) ▢ₐ e1 ⦈)) a0 H c v1 e0 | TT => fun (_ : val Ξ” a) (_ : ev_ctx Ξ” b) => apply_noConfusion ΞΉ (a β†’ b) (False_rect (state Ξ” + nf Ξ”)) | @Lam _ a0 b0 t => fun (v1 : val Ξ” a) (e0 : ev_ctx Ξ” b) => apply_noConfusion (a0 β†’ b0) (a β†’ b) (DepElim.eq_simplification_sigma1 a0 a b0 b (fun H : a0 = a => DepElim.solution_left a (fun (b1 : ty0) (t0 : term (Ξ” β–Άβ‚“ ... β–Άβ‚“ + a) b1) (v2 : val Ξ” a) (e1 : ev_ctx Ξ” b) (H0 : b1 = b) => DepElim.solution_left b (fun (t1 : term ... b) (v3 : val Ξ” a) => inl ∘ Cut (...)) b1 H0 t0 v2 e1) a0 H b0 t v1 e0)) end v e eq_refl end (v `α΅₯βŠ› e) with | inl x => TauF (Structure.iter (fun pat : T1 => let 'T1_0 as x0 := pat return (state Ξ” -> itree Event.emptyβ‚‘ ((fun _ : T1 => state Ξ”) +α΅’ (fun _ : T1 => nf Ξ”)) x0) in ret_delay ∘ eval_step) T1_0 x) | inr y => RetF y end
Ξ“: t_ctx
a, b: ty0
v: val Ξ“ (a β†’ b)
i: Ξ“ βˆ‹ Β¬ (a β†’ b)
Ξ”: t_ctx
R: relα΅’ (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”)) (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”))
CIH: forall (c : state Ξ“) (a : Ξ“ =[ val_m ]> Ξ”), it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R T1_0 (stlc_eval (m_var c `β‚œβŠ› a β‹… m_obs c `β‚‘βŠ› a)) (bind_delay' (stlc_eval c) (fun n : Obs.nf obs_op val_m Ξ“ => stlc_eval (m_var (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚œβŠ› a β‹… m_obs (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚‘βŠ› a)))
e: Ξ“ =[ val_m ]> Ξ”
H: {| pr1 := ORet; pr2 := (! ▢ₐ v)%asgn |} = {| pr1 := ORet; pr2 := (! ▢ₐ v)%asgn |}
v0: val Ξ” a
v1: ev_ctx Ξ” b

it_eqF Event.emptyβ‚‘ (fun _ : T1 => nf_eq) (it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R) T1_0 match match v `α΅₯βŠ› e in (val _ t) return (val Ξ” a -> ev_ctx Ξ” b -> t = a β†’ b -> state Ξ” + nf Ξ”) with | @Var _ a0 c => fun (v0 : val Ξ” a) (e0 : ev_ctx Ξ” b) (H : a0 = a β†’ b) => DepElim.solution_left (a β†’ b) (fun (c0 : cvar Ξ” + (a β†’ b)) (v1 : val Ξ” a) (e1 : ev_ctx Ξ” b) => inr (c0 β‹… OApp ⦇ (! ▢ₐ v1) ▢ₐ e1 ⦈)) a0 H c v0 e0 | TT => fun (_ : val Ξ” a) (_ : ev_ctx Ξ” b) => apply_noConfusion ΞΉ (a β†’ b) (False_rect (state Ξ” + nf Ξ”)) | @Lam _ a0 b0 t => fun (v0 : val Ξ” a) (e0 : ev_ctx Ξ” b) => apply_noConfusion (a0 β†’ b0) (a β†’ b) (DepElim.eq_simplification_sigma1 a0 a b0 b (fun H : a0 = a => DepElim.solution_left a (fun (b1 : ty0) (t0 : term (Ξ” β–Άβ‚“ + (a β†’ b1) β–Άβ‚“ + a) b1) (v1 : val Ξ” a) (e1 : ev_ctx Ξ” b) (H0 : b1 = b) => DepElim.solution_left b (fun (t1 : term (Ξ” β–Άβ‚“ + ... β–Άβ‚“ + a) b) (v2 : val Ξ” a) => inl ∘ Cut (t1 /[ Lam t1, v2])) b1 H0 t0 v1 e1) a0 H b0 t v0 e0)) end v0 v1 eq_refl with | inl x => TauF (Structure.iter (fun pat : T1 => let 'T1_0 as x0 := pat return (state Ξ” -> itree Event.emptyβ‚‘ ((fun _ : T1 => state Ξ”) +α΅’ (fun _ : T1 => nf Ξ”)) x0) in ret_delay ∘ eval_step) T1_0 x) | inr y => RetF y end match match v `α΅₯βŠ› e in (val _ t) return (val Ξ” a -> ev_ctx Ξ” b -> t = a β†’ b -> state Ξ” + nf Ξ”) with | @Var _ a0 c => fun (v0 : val Ξ” a) (e0 : ev_ctx Ξ” b) (H : a0 = a β†’ b) => DepElim.solution_left (a β†’ b) (fun (c0 : cvar Ξ” + (a β†’ b)) (v1 : val Ξ” a) (e1 : ev_ctx Ξ” b) => inr (c0 β‹… OApp ⦇ (! ▢ₐ v1) ▢ₐ e1 ⦈)) a0 H c v0 e0 | TT => fun (_ : val Ξ” a) (_ : ev_ctx Ξ” b) => apply_noConfusion ΞΉ (a β†’ b) (False_rect (state Ξ” + nf Ξ”)) | @Lam _ a0 b0 t => fun (v0 : val Ξ” a) (e0 : ev_ctx Ξ” b) => apply_noConfusion (a0 β†’ b0) (a β†’ b) (DepElim.eq_simplification_sigma1 a0 a b0 b (fun H : a0 = a => DepElim.solution_left a (fun (b1 : ty0) (t0 : term (Ξ” β–Άβ‚“ + (a β†’ b1) β–Άβ‚“ + a) b1) (v1 : val Ξ” a) (e1 : ev_ctx Ξ” b) (H0 : b1 = b) => DepElim.solution_left b (fun (t1 : term (Ξ” β–Άβ‚“ + ... β–Άβ‚“ + a) b) (v2 : val Ξ” a) => inl ∘ Cut (t1 /[ Lam t1, v2])) b1 H0 t0 v1 e1) a0 H b0 t v0 e0)) end v0 v1 eq_refl with | inl x => TauF (Structure.iter (fun pat : T1 => let 'T1_0 as x0 := pat return (state Ξ” -> itree Event.emptyβ‚‘ ((fun _ : T1 => state Ξ”) +α΅’ (fun _ : T1 => nf Ξ”)) x0) in ret_delay ∘ eval_step) T1_0 x) | inr y => RetF y end
Ξ“: t_ctx
a, b: ty0
i: Ξ“ βˆ‹ Β¬ (a β†’ b)
Ξ”: t_ctx
R: relα΅’ (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”)) (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”))
CIH: forall (c : state Ξ“) (a : Ξ“ =[ val_m ]> Ξ”), it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R T1_0 (stlc_eval (m_var c `β‚œβŠ› a β‹… m_obs c `β‚‘βŠ› a)) (bind_delay' (stlc_eval c) (fun n : Obs.nf obs_op val_m Ξ“ => stlc_eval (m_var (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚œβŠ› a β‹… m_obs (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚‘βŠ› a)))
e: Ξ“ =[ val_m ]> Ξ”
v0: val Ξ” a
v1: ev_ctx Ξ” b
v': val Ξ” (a β†’ b)

it_eqF Event.emptyβ‚‘ (fun _ : T1 => nf_eq) (it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R) T1_0 match match v' in (val _ t) return (val Ξ” a -> ev_ctx Ξ” b -> t = a β†’ b -> state Ξ” + nf Ξ”) with | @Var _ a0 c => fun (v0 : val Ξ” a) (e0 : ev_ctx Ξ” b) (H : a0 = a β†’ b) => DepElim.solution_left (a β†’ b) (fun (c0 : cvar Ξ” + (a β†’ b)) (v1 : val Ξ” a) (e1 : ev_ctx Ξ” b) => inr (c0 β‹… OApp ⦇ (! ▢ₐ v1) ▢ₐ e1 ⦈)) a0 H c v0 e0 | TT => fun (_ : val Ξ” a) (_ : ev_ctx Ξ” b) => apply_noConfusion ΞΉ (a β†’ b) (False_rect (state Ξ” + nf Ξ”)) | @Lam _ a0 b0 t => fun (v0 : val Ξ” a) (e0 : ev_ctx Ξ” b) => apply_noConfusion (a0 β†’ b0) (a β†’ b) (DepElim.eq_simplification_sigma1 a0 a b0 b (fun H : a0 = a => DepElim.solution_left a (fun (b1 : ty0) (t0 : term (Ξ” β–Άβ‚“ + (a β†’ b1) β–Άβ‚“ + a) b1) (v1 : val Ξ” a) (e1 : ev_ctx Ξ” b) (H0 : b1 = b) => DepElim.solution_left b (fun (t1 : term (Ξ” β–Άβ‚“ + ... β–Άβ‚“ + a) b) (v2 : val Ξ” a) => inl ∘ Cut (t1 /[ Lam t1, v2])) b1 H0 t0 v1 e1) a0 H b0 t v0 e0)) end v0 v1 eq_refl with | inl x => TauF (Structure.iter (fun pat : T1 => let 'T1_0 as x0 := pat return (state Ξ” -> itree Event.emptyβ‚‘ ((fun _ : T1 => state Ξ”) +α΅’ (fun _ : T1 => nf Ξ”)) x0) in ret_delay ∘ eval_step) T1_0 x) | inr y => RetF y end match match v' in (val _ t) return (val Ξ” a -> ev_ctx Ξ” b -> t = a β†’ b -> state Ξ” + nf Ξ”) with | @Var _ a0 c => fun (v0 : val Ξ” a) (e0 : ev_ctx Ξ” b) (H : a0 = a β†’ b) => DepElim.solution_left (a β†’ b) (fun (c0 : cvar Ξ” + (a β†’ b)) (v1 : val Ξ” a) (e1 : ev_ctx Ξ” b) => inr (c0 β‹… OApp ⦇ (! ▢ₐ v1) ▢ₐ e1 ⦈)) a0 H c v0 e0 | TT => fun (_ : val Ξ” a) (_ : ev_ctx Ξ” b) => apply_noConfusion ΞΉ (a β†’ b) (False_rect (state Ξ” + nf Ξ”)) | @Lam _ a0 b0 t => fun (v0 : val Ξ” a) (e0 : ev_ctx Ξ” b) => apply_noConfusion (a0 β†’ b0) (a β†’ b) (DepElim.eq_simplification_sigma1 a0 a b0 b (fun H : a0 = a => DepElim.solution_left a (fun (b1 : ty0) (t0 : term (Ξ” β–Άβ‚“ + (a β†’ b1) β–Άβ‚“ + a) b1) (v1 : val Ξ” a) (e1 : ev_ctx Ξ” b) (H0 : b1 = b) => DepElim.solution_left b (fun (t1 : term (Ξ” β–Άβ‚“ + ... β–Άβ‚“ + a) b) (v2 : val Ξ” a) => inl ∘ Cut (t1 /[ Lam t1, v2])) b1 H0 t0 v1 e1) a0 H b0 t v0 e0)) end v0 v1 eq_refl with | inl x => TauF (Structure.iter (fun pat : T1 => let 'T1_0 as x0 := pat return (state Ξ” -> itree Event.emptyβ‚‘ ((fun _ : T1 => state Ξ”) +α΅’ (fun _ : T1 => nf Ξ”)) x0) in ret_delay ∘ eval_step) T1_0 x) | inr y => RetF y end
dependent elimination v'; cbn; auto.
Ξ“: t_ctx
a1: ty0
v: val Ξ“ a1
b: ty0
t: term Ξ“ (a1 β†’ b)
Ο€: ev_ctx Ξ“ b
Ξ”: t_ctx
R: relα΅’ (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”)) (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”))
CIH: forall (c : state Ξ“) (a : Ξ“ =[ val_m ]> Ξ”), it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R T1_0 (stlc_eval (m_var c `β‚œβŠ› a β‹… m_obs c `β‚‘βŠ› a)) (bind_delay' (stlc_eval c) (fun n : Obs.nf obs_op val_m Ξ“ => stlc_eval (m_var (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚œβŠ› a β‹… m_obs (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚‘βŠ› a)))
e: Ξ“ =[ val_m ]> Ξ”
H: K2 v Ο€ = K2 v Ο€

it_eqF Event.emptyβ‚‘ (fun _ : T1 => nf_eq) (it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R) T1_0 (TauF (Structure.iter (fun pat : T1 => let 'T1_0 as x := pat return (state Ξ” -> itree Event.emptyβ‚‘ ((fun _ : T1 => state Ξ”) +α΅’ (fun _ : T1 => nf Ξ”)) x) in ret_delay ∘ eval_step) T1_0 (t `β‚œβŠ› e β‹… K2 (v `α΅₯βŠ› e) (Ο€ `β‚‘βŠ› e)))) (TauF (Structure.subst (fun pat : T1 => let 'T1_0 as x := pat return (Obs.nf obs_op val_m Ξ“ -> itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”) x) in fun y : Obs.nf obs_op val_m Ξ“ => stlc_eval (m_var (obs_app (a_id (nf_ty y) (nf_var y)) (nf_obs y) (nf_args y)) `β‚œβŠ› e β‹… m_obs (obs_app (a_id (nf_ty y) (nf_var y)) (nf_obs y) (nf_args y)) `β‚‘βŠ› e)) T1_0 (Structure.iter (fun pat : T1 => let 'T1_0 as x := pat return (state Ξ“ -> itree Event.emptyβ‚‘ ((fun _ : T1 => state Ξ“) +α΅’ (fun _ : T1 => nf Ξ“)) x) in ret_delay ∘ eval_step) T1_0 (t β‹… K2 v Ο€))))
econstructor; refold_eval; apply CIH.
Ξ“: t_ctx
a2, b0: ty0
i: Ξ“ βˆ‹ + (a2 β†’ b0)
v: val Ξ“ a2
Ο€: ev_ctx Ξ“ b0
Ξ”: t_ctx
R: relα΅’ (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”)) (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”))
CIH: forall (c : state Ξ“) (a : Ξ“ =[ val_m ]> Ξ”), it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R T1_0 (stlc_eval (m_var c `β‚œβŠ› a β‹… m_obs c `β‚‘βŠ› a)) (bind_delay' (stlc_eval c) (fun n : Obs.nf obs_op val_m Ξ“ => stlc_eval (m_var (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚œβŠ› a β‹… m_obs (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚‘βŠ› a)))
e: Ξ“ =[ val_m ]> Ξ”
H: {| pr1 := OApp; pr2 := ((! ▢ₐ v) ▢ₐ Ο€)%asgn |} = {| pr1 := OApp; pr2 := ((! ▢ₐ v) ▢ₐ Ο€)%asgn |}

it_eqF Event.emptyβ‚‘ (fun _ : T1 => nf_eq) (it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R) T1_0 match match e + (a2 β†’ b0) i in (val _ t) return (val Ξ” a2 -> ev_ctx Ξ” b0 -> t = a2 β†’ b0 -> state Ξ” + nf Ξ”) with | @Var _ a c => fun (v0 : val Ξ” a2) (e0 : ev_ctx Ξ” b0) (H : a = a2 β†’ b0) => DepElim.solution_left (a2 β†’ b0) (fun (c0 : cvar Ξ” + (a2 β†’ b0)) (v1 : val Ξ” a2) (e1 : ev_ctx Ξ” b0) => inr (c0 β‹… OApp ⦇ (! ▢ₐ v1) ▢ₐ e1 ⦈)) a H c v0 e0 | TT => fun (_ : val Ξ” a2) (_ : ev_ctx Ξ” b0) => apply_noConfusion ΞΉ (a2 β†’ b0) (False_rect (state Ξ” + nf Ξ”)) | @Lam _ a b t => fun (v0 : val Ξ” a2) (e0 : ev_ctx Ξ” b0) => apply_noConfusion (a β†’ b) (a2 β†’ b0) (DepElim.eq_simplification_sigma1 a a2 b b0 (fun H : a = a2 => DepElim.solution_left a2 (fun (b1 : ty0) (t0 : term (Ξ” β–Άβ‚“ + (a2 β†’ b1) β–Άβ‚“ + a2) b1) (v1 : val Ξ” a2) (e1 : ev_ctx Ξ” b0) (H0 : b1 = b0) => DepElim.solution_left b0 (fun (t1 : term (Ξ” β–Άβ‚“ + ... β–Άβ‚“ + a2) b0) (v2 : val Ξ” a2) => inl ∘ Cut (t1 /[ Lam t1, v2])) b1 H0 t0 v1 e1) a H b t v0 e0)) end (v `α΅₯βŠ› e) (Ο€ `β‚‘βŠ› e) eq_refl with | inl x => TauF (Structure.iter (fun pat : T1 => let 'T1_0 as x0 := pat return (state Ξ” -> itree Event.emptyβ‚‘ ((fun _ : T1 => state Ξ”) +α΅’ (fun _ : T1 => nf Ξ”)) x0) in ret_delay ∘ eval_step) T1_0 x) | inr y => RetF y end match match e + (a2 β†’ b0) i in (val _ t) return (val Ξ” a2 -> ev_ctx Ξ” b0 -> t = a2 β†’ b0 -> state Ξ” + nf Ξ”) with | @Var _ a c => fun (v0 : val Ξ” a2) (e0 : ev_ctx Ξ” b0) (H : a = a2 β†’ b0) => DepElim.solution_left (a2 β†’ b0) (fun (c0 : cvar Ξ” + (a2 β†’ b0)) (v1 : val Ξ” a2) (e1 : ev_ctx Ξ” b0) => inr (c0 β‹… OApp ⦇ (! ▢ₐ v1) ▢ₐ e1 ⦈)) a H c v0 e0 | TT => fun (_ : val Ξ” a2) (_ : ev_ctx Ξ” b0) => apply_noConfusion ΞΉ (a2 β†’ b0) (False_rect (state Ξ” + nf Ξ”)) | @Lam _ a b t => fun (v0 : val Ξ” a2) (e0 : ev_ctx Ξ” b0) => apply_noConfusion (a β†’ b) (a2 β†’ b0) (DepElim.eq_simplification_sigma1 a a2 b b0 (fun H : a = a2 => DepElim.solution_left a2 (fun (b1 : ty0) (t0 : term (Ξ” β–Άβ‚“ + (a2 β†’ b1) β–Άβ‚“ + a2) b1) (v1 : val Ξ” a2) (e1 : ev_ctx Ξ” b0) (H0 : b1 = b0) => DepElim.solution_left b0 (fun (t1 : term (Ξ” β–Άβ‚“ + ... β–Άβ‚“ + a2) b0) (v2 : val Ξ” a2) => inl ∘ Cut (t1 /[ Lam t1, v2])) b1 H0 t0 v1 e1) a H b t v0 e0)) end (v `α΅₯βŠ› e) (Ο€ `β‚‘βŠ› e) eq_refl with | inl x => TauF (Structure.iter (fun pat : T1 => let 'T1_0 as x0 := pat return (state Ξ” -> itree Event.emptyβ‚‘ ((fun _ : T1 => state Ξ”) +α΅’ (fun _ : T1 => nf Ξ”)) x0) in ret_delay ∘ eval_step) T1_0 x) | inr y => RetF y end
Ξ“: t_ctx
a2, b0: ty0
v: val Ξ“ a2
Ο€: ev_ctx Ξ“ b0
Ξ”: t_ctx
R: relα΅’ (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”)) (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”))
CIH: forall (c : state Ξ“) (a : Ξ“ =[ val_m ]> Ξ”), it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R T1_0 (stlc_eval (m_var c `β‚œβŠ› a β‹… m_obs c `β‚‘βŠ› a)) (bind_delay' (stlc_eval c) (fun n : Obs.nf obs_op val_m Ξ“ => stlc_eval (m_var (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚œβŠ› a β‹… m_obs (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚‘βŠ› a)))
e: Ξ“ =[ val_m ]> Ξ”
vv: val_m Ξ” + (a2 β†’ b0)

it_eqF Event.emptyβ‚‘ (fun _ : T1 => nf_eq) (it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R) T1_0 match match vv in (val _ t) return (val Ξ” a2 -> ev_ctx Ξ” b0 -> t = a2 β†’ b0 -> state Ξ” + nf Ξ”) with | @Var _ a c => fun (v0 : val Ξ” a2) (e0 : ev_ctx Ξ” b0) (H : a = a2 β†’ b0) => DepElim.solution_left (a2 β†’ b0) (fun (c0 : cvar Ξ” + (a2 β†’ b0)) (v1 : val Ξ” a2) (e1 : ev_ctx Ξ” b0) => inr (c0 β‹… OApp ⦇ (! ▢ₐ v1) ▢ₐ e1 ⦈)) a H c v0 e0 | TT => fun (_ : val Ξ” a2) (_ : ev_ctx Ξ” b0) => apply_noConfusion ΞΉ (a2 β†’ b0) (False_rect (state Ξ” + nf Ξ”)) | @Lam _ a b t => fun (v0 : val Ξ” a2) (e0 : ev_ctx Ξ” b0) => apply_noConfusion (a β†’ b) (a2 β†’ b0) (DepElim.eq_simplification_sigma1 a a2 b b0 (fun H : a = a2 => DepElim.solution_left a2 (fun (b1 : ty0) (t0 : term (Ξ” β–Άβ‚“ + (a2 β†’ b1) β–Άβ‚“ + a2) b1) (v1 : val Ξ” a2) (e1 : ev_ctx Ξ” b0) (H0 : b1 = b0) => DepElim.solution_left b0 (fun (t1 : term (Ξ” β–Άβ‚“ + ... β–Άβ‚“ + a2) b0) (v2 : val Ξ” a2) => inl ∘ Cut (t1 /[ Lam t1, v2])) b1 H0 t0 v1 e1) a H b t v0 e0)) end (v `α΅₯βŠ› e) (Ο€ `β‚‘βŠ› e) eq_refl with | inl x => TauF (Structure.iter (fun pat : T1 => let 'T1_0 as x0 := pat return (state Ξ” -> itree Event.emptyβ‚‘ ((fun _ : T1 => state Ξ”) +α΅’ (fun _ : T1 => nf Ξ”)) x0) in ret_delay ∘ eval_step) T1_0 x) | inr y => RetF y end match match vv in (val _ t) return (val Ξ” a2 -> ev_ctx Ξ” b0 -> t = a2 β†’ b0 -> state Ξ” + nf Ξ”) with | @Var _ a c => fun (v0 : val Ξ” a2) (e0 : ev_ctx Ξ” b0) (H : a = a2 β†’ b0) => DepElim.solution_left (a2 β†’ b0) (fun (c0 : cvar Ξ” + (a2 β†’ b0)) (v1 : val Ξ” a2) (e1 : ev_ctx Ξ” b0) => inr (c0 β‹… OApp ⦇ (! ▢ₐ v1) ▢ₐ e1 ⦈)) a H c v0 e0 | TT => fun (_ : val Ξ” a2) (_ : ev_ctx Ξ” b0) => apply_noConfusion ΞΉ (a2 β†’ b0) (False_rect (state Ξ” + nf Ξ”)) | @Lam _ a b t => fun (v0 : val Ξ” a2) (e0 : ev_ctx Ξ” b0) => apply_noConfusion (a β†’ b) (a2 β†’ b0) (DepElim.eq_simplification_sigma1 a a2 b b0 (fun H : a = a2 => DepElim.solution_left a2 (fun (b1 : ty0) (t0 : term (Ξ” β–Άβ‚“ + (a2 β†’ b1) β–Άβ‚“ + a2) b1) (v1 : val Ξ” a2) (e1 : ev_ctx Ξ” b0) (H0 : b1 = b0) => DepElim.solution_left b0 (fun (t1 : term (Ξ” β–Άβ‚“ + ... β–Άβ‚“ + a2) b0) (v2 : val Ξ” a2) => inl ∘ Cut (t1 /[ Lam t1, v2])) b1 H0 t0 v1 e1) a H b t v0 e0)) end (v `α΅₯βŠ› e) (Ο€ `β‚‘βŠ› e) eq_refl with | inl x => TauF (Structure.iter (fun pat : T1 => let 'T1_0 as x0 := pat return (state Ξ” -> itree Event.emptyβ‚‘ ((fun _ : T1 => state Ξ”) +α΅’ (fun _ : T1 => nf Ξ”)) x0) in ret_delay ∘ eval_step) T1_0 x) | inr y => RetF y end
dependent elimination vv; cbn; auto.
Ξ“: t_ctx
a0, b: ty0
t: term (Ξ“ β–Άβ‚“ + (a0 β†’ b) β–Άβ‚“ + a0) b
v: val Ξ“ a0
Ο€: ev_ctx Ξ“ b
Ξ”: t_ctx
R: relα΅’ (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”)) (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”))
CIH: forall (c : state Ξ“) (a : Ξ“ =[ val_m ]> Ξ”), it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R T1_0 (stlc_eval (m_var c `β‚œβŠ› a β‹… m_obs c `β‚‘βŠ› a)) (bind_delay' (stlc_eval c) (fun n : Obs.nf obs_op val_m Ξ“ => stlc_eval (m_var (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚œβŠ› a β‹… m_obs (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚‘βŠ› a)))
e: Ξ“ =[ val_m ]> Ξ”

it_eqF Event.emptyβ‚‘ (fun _ : T1 => nf_eq) (it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R) T1_0 (TauF (Structure.iter (fun pat : T1 => let 'T1_0 as x := pat return (state Ξ” -> itree Event.emptyβ‚‘ ((fun _ : T1 => state Ξ”) +α΅’ (fun _ : T1 => nf Ξ”)) x) in ret_delay ∘ eval_step) T1_0 ((t `β‚œβŠ› a_shift2 e /[ Lam (t `β‚œβŠ› a_shift2 e), v `α΅₯βŠ› e]) β‹… Ο€ `β‚‘βŠ› e))) (TauF (Structure.subst (fun pat : T1 => let 'T1_0 as x := pat return (Obs.nf obs_op val_m Ξ“ -> itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”) x) in fun y : Obs.nf obs_op val_m Ξ“ => stlc_eval (m_var (obs_app (a_id (nf_ty y) (nf_var y)) (nf_obs y) (nf_args y)) `β‚œβŠ› e β‹… m_obs (obs_app (a_id (nf_ty y) (nf_var y)) (nf_obs y) (nf_args y)) `β‚‘βŠ› e)) T1_0 (Structure.iter (fun pat : T1 => let 'T1_0 as x := pat return (state Ξ“ -> itree Event.emptyβ‚‘ ((fun _ : T1 => state Ξ“) +α΅’ (fun _ : T1 => nf Ξ“)) x) in ret_delay ∘ eval_step) T1_0 ((t /[ Lam t, v]) β‹… Ο€))))
Ξ“: t_ctx
a0, b: ty0
t: term (Ξ“ β–Άβ‚“ + (a0 β†’ b) β–Άβ‚“ + a0) b
v: val Ξ“ a0
Ο€: ev_ctx Ξ“ b
Ξ”: t_ctx
R: relα΅’ (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”)) (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”))
CIH: forall (c : state Ξ“) (a : Ξ“ =[ val_m ]> Ξ”), it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R T1_0 (stlc_eval (m_var c `β‚œβŠ› a β‹… m_obs c `β‚‘βŠ› a)) (bind_delay' (stlc_eval c) (fun n : Obs.nf obs_op val_m Ξ“ => stlc_eval (m_var (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚œβŠ› a β‹… m_obs (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚‘βŠ› a)))
e: Ξ“ =[ val_m ]> Ξ”

it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R T1_0 (stlc_eval ((t `β‚œβŠ› a_shift2 e /[ (Lam t : val_m Ξ“ + (a0 β†’ b)) α΅₯βŠ› e, (v : val_m Ξ“ + a0) α΅₯βŠ› e]) β‹… Ο€ `β‚‘βŠ› e)) (bind_delay' (stlc_eval ((t /[ Lam t, v]) β‹… Ο€)) (fun y : Obs.nf obs_op val_m Ξ“ => stlc_eval (m_var (obs_app (a_id (nf_ty y) (nf_var y)) (nf_obs y) (nf_args y)) `β‚œβŠ› e β‹… m_obs (obs_app (a_id (nf_ty y) (nf_var y)) (nf_obs y) (nf_args y)) `β‚‘βŠ› e)))
Ξ“: t_ctx
a0, b: ty0
t: term (Ξ“ β–Άβ‚“ + (a0 β†’ b) β–Άβ‚“ + a0) b
v: val Ξ“ a0
Ο€: ev_ctx Ξ“ b
Ξ”: t_ctx
R: relα΅’ (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”)) (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”))
CIH: forall (c : state Ξ“) (a : Ξ“ =[ val_m ]> Ξ”), it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R T1_0 (stlc_eval (m_var c `β‚œβŠ› a β‹… m_obs c `β‚‘βŠ› a)) (bind_delay' (stlc_eval c) (fun n : Obs.nf obs_op val_m Ξ“ => stlc_eval (m_var (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚œβŠ› a β‹… m_obs (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚‘βŠ› a)))
e: Ξ“ =[ val_m ]> Ξ”

it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R T1_0 (stlc_eval ((t /[ Lam t, v]) `β‚œβŠ› e β‹… Ο€ `β‚‘βŠ› e)) (bind_delay' (stlc_eval ((t /[ Lam t, v]) β‹… Ο€)) (fun y : Obs.nf obs_op val_m Ξ“ => stlc_eval (m_var (obs_app (a_id (nf_ty y) (nf_var y)) (nf_obs y) (nf_args y)) `β‚œβŠ› e β‹… m_obs (obs_app (a_id (nf_ty y) (nf_var y)) (nf_obs y) (nf_args y)) `β‚‘βŠ› e)))
apply CIH.
Ξ“: t_ctx
b, a0: ty0
t1: term Ξ“ (a0 β†’ b)
t2: term Ξ“ a0
Ο€: ev_ctx Ξ“ b
Ξ”: t_ctx
R: relα΅’ (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”)) (itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”))
CIH: forall (c : state Ξ“) (a : Ξ“ =[ val_m ]> Ξ”), it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R T1_0 (stlc_eval (m_var c `β‚œβŠ› a β‹… m_obs c `β‚‘βŠ› a)) (bind_delay' (stlc_eval c) (fun n : Obs.nf obs_op val_m Ξ“ => stlc_eval (m_var (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚œβŠ› a β‹… m_obs (obs_app (a_id (nf_ty n) (nf_var n)) (nf_obs n) (nf_args n)) `β‚‘βŠ› a)))
e: Ξ“ =[ val_m ]> Ξ”

it_eqF Event.emptyβ‚‘ (fun _ : T1 => nf_eq) (it_eq_t Event.emptyβ‚‘ (fun _ : T1 => nf_eq) R) T1_0 (TauF (Structure.iter (fun pat : T1 => let 'T1_0 as x := pat return (state Ξ” -> itree Event.emptyβ‚‘ ((fun _ : T1 => state Ξ”) +α΅’ (fun _ : T1 => nf Ξ”)) x) in ret_delay ∘ eval_step) T1_0 (t2 `β‚œβŠ› e β‹… K1 (t1 `β‚œβŠ› e) (Ο€ `β‚‘βŠ› e)))) (TauF (Structure.subst (fun pat : T1 => let 'T1_0 as x := pat return (Obs.nf obs_op val_m Ξ“ -> itree Event.emptyβ‚‘ (fun _ : T1 => Obs.nf obs_op val_m Ξ”) x) in fun y : Obs.nf obs_op val_m Ξ“ => stlc_eval (m_var (obs_app (a_id (nf_ty y) (nf_var y)) (nf_obs y) (nf_args y)) `β‚œβŠ› e β‹… m_obs (obs_app (a_id (nf_ty y) (nf_var y)) (nf_obs y) (nf_args y)) `β‚‘βŠ› e)) T1_0 (Structure.iter (fun pat : T1 => let 'T1_0 as x := pat return (state Ξ“ -> itree Event.emptyβ‚‘ ((fun _ : T1 => state Ξ“) +α΅’ (fun _ : T1 => nf Ξ“)) x) in ret_delay ∘ eval_step) T1_0 (t2 β‹… K1 t1 Ο€))))
econstructor; refold_eval; apply CIH.

Just like the above proof had the flavor of a composition law of module, this one has the flavor of an identity law. It states that evaluating a normal form is the identity computation.

  
Ξ“: t_ctx
u: Obs.nf obs_op val_m Ξ“

stlc_eval (obs_app (a_id (nf_ty u) (nf_var u)) (nf_obs u) (nf_args u)) ≋ ret_delay u
Ξ“: t_ctx
a: ty
i: Ξ“ βˆ‹ a
p: obs_op a
Ξ³: dom p =[ val_m ]> Ξ“

stlc_eval (obs_app (a_id a i) p (nf_args (i β‹… p ⦇ Ξ³ ⦈))) ≋ ret_delay (i β‹… p ⦇ Ξ³ ⦈)
Ξ“: t_ctx
a0: ty0
i: Ξ“ βˆ‹ Β¬ a0
Ξ³: dom ORet =[ val_m ]> Ξ“

stlc_eval (Ξ³ + a0 Ctx.top β‹… K0 i) ≋ ret_delay (i β‹… ORet ⦇ Ξ³ ⦈)
Ξ“: t_ctx
a1, b: ty0
i: Ξ“ βˆ‹ + (a1 β†’ b)
Ξ³: dom OApp =[ val_m ]> Ξ“
stlc_eval (Var i β‹… K2 (Ξ³ + a1 (pop Ctx.top)) (Ξ³ Β¬ b Ctx.top)) ≋ ret_delay (i β‹… OApp ⦇ Ξ³ ⦈)
Ξ“: t_ctx
a0: ty0
i: Ξ“ βˆ‹ Β¬ a0
Ξ³: dom ORet =[ val_m ]> Ξ“

nf_eq (i β‹… ORet ⦇ ! ▢ₐ Ξ³ + a0 Ctx.top ⦈) (i β‹… ORet ⦇ Ξ³ ⦈)
Ξ“: t_ctx
a1, b: ty0
i: Ξ“ βˆ‹ + (a1 β†’ b)
Ξ³: dom OApp =[ val_m ]> Ξ“
nf_eq (i β‹… OApp ⦇ (! ▢ₐ Ξ³ + a1 (pop Ctx.top)) ▢ₐ Ξ³ Β¬ b Ctx.top ⦈) (i β‹… OApp ⦇ Ξ³ ⦈)
all: econstructor; intros ? v; do 3 (dependent elimination v; auto).

This last proof is the technical condition we hinted at. It is a proof of well-foundedness of some relation, and what it amounts to is that if we repeatedly instantiate the head variable of a normal form by a value which is not a variable, after a finite number of times doing so we will eventually reach something that is not a normal form.

For our calculus this number is at most 2, the pathological state being ⟨ x | y ⟩, which starts by being stuck on y, but when instanciating by some non-variable Ο€, ⟨ x | Ο€ ⟩ is still stuck, this time on x. After another step it will definitely be unstuck and evaluation will be able to do a reduction step.

It is slightly tedious to prove but amount again to a "proof by case splitting".

  

well_founded head_inst_nostep
x: ty
p: obs x

Acc head_inst_nostep (x,' p)
a0, b: ty0

forall y : {x : ty & obs x}, head_inst_nostep y (+ (a0 β†’ b),' OApp) -> Acc head_inst_nostep y
a: ty0
forall y : {x : ty & obs x}, head_inst_nostep y (Β¬ a,' ORet) -> Acc head_inst_nostep y
a0, b: ty0

forall y : {x : ty & obs x}, head_inst_nostep y (+ (a0 β†’ b),' OApp) -> Acc head_inst_nostep y
a0, b: ty0
z: ty
p: obs z
H: head_inst_nostep (z,' p) (+ (a0 β†’ b),' OApp)

Acc head_inst_nostep (z,' p)
a0, b, a: ty0
Ξ“: t_ctx
v: val_m Ξ“ + (a0 β†’ b)
a1: dom OApp =[ val_m ]> Ξ“
i: Ξ“ βˆ‹ projT1 (Β¬ a,' ORet)
t0: is_var v -> T0
i0: evalβ‚’ (v βŠ™ OApp β¦— a1 ⦘) β‰Š ret_delay (i β‹… projT2 (Β¬ a,' ORet))

Acc head_inst_nostep (Β¬ a,' ORet)
a0, b, a1, b0: ty0
Ξ“: t_ctx
v: val_m Ξ“ + (a0 β†’ b)
a: dom OApp =[ val_m ]> Ξ“
i: Ξ“ βˆ‹ projT1 (+ (a1 β†’ b0),' OApp)
t0: is_var v -> T0
i0: evalβ‚’ (v βŠ™ OApp β¦— a ⦘) β‰Š ret_delay (i β‹… projT2 (+ (a1 β†’ b0),' OApp))
Acc head_inst_nostep (+ (a1 β†’ b0),' OApp)
a3, b0, a: ty0
Ξ“: t_ctx
t1: term (Ξ“ β–Άβ‚“ + (a3 β†’ b0) β–Άβ‚“ + a3) b0
a1: dom OApp =[ val_m ]> Ξ“
i: Ξ“ βˆ‹ projT1 (Β¬ a,' ORet)
t0: is_var (Lam t1) -> T0
i0: evalβ‚’ (Lam t1 βŠ™ OApp β¦— a1 ⦘) β‰Š ret_delay (i β‹… projT2 (Β¬ a,' ORet))

Acc head_inst_nostep (Β¬ a,' ORet)
a3, b1, a1, b0: ty0
Ξ“: t_ctx
t1: term (Ξ“ β–Άβ‚“ + (a3 β†’ b1) β–Άβ‚“ + a3) b1
a: dom OApp =[ val_m ]> Ξ“
i: Ξ“ βˆ‹ projT1 (+ (a1 β†’ b0),' OApp)
t0: is_var (Lam t1) -> T0
i0: evalβ‚’ (Lam t1 βŠ™ OApp β¦— a ⦘) β‰Š ret_delay (i β‹… projT2 (+ (a1 β†’ b0),' OApp))
Acc head_inst_nostep (+ (a1 β†’ b0),' OApp)
all: apply it_eq_step in i0; now inversion i0.
a: ty0

forall y : {x : ty & obs x}, head_inst_nostep y (Β¬ a,' ORet) -> Acc head_inst_nostep y
a: ty0
z: ty
p: obs z
H: head_inst_nostep (z,' p) (Β¬ a,' ORet)

Acc head_inst_nostep (z,' p)
a, a0: ty0
Ξ“: t_ctx
v: ev_ctx Ξ“ a
a1: (βˆ…β‚“ β–Άβ‚“ + a) =[ val_m ]> Ξ“
i: cvar Ξ“ Β¬ a0
t0: is_var v -> T0
i0: evalβ‚’ (a1 + a Ctx.top β‹… v) β‰Š ret_delay (i β‹… ORet)

Acc head_inst_nostep (Β¬ a0,' ORet)
a, a1, b: ty0
Ξ“: t_ctx
v: ev_ctx Ξ“ a
a0: (βˆ…β‚“ β–Άβ‚“ + a) =[ val_m ]> Ξ“
i: cvar Ξ“ + (a1 β†’ b)
t0: is_var v -> T0
i0: evalβ‚’ (a0 + a Ctx.top β‹… v) β‰Š ret_delay (i β‹… OApp)
Acc head_inst_nostep (+ (a1 β†’ b),' OApp)
a3, a0: ty0
Ξ“: t_ctx
b: ty0
t1: term Ξ“ (a3 β†’ b)
e: ev_ctx Ξ“ b
a1: (βˆ…β‚“ β–Άβ‚“ + a3) =[ val_m ]> Ξ“
i: cvar Ξ“ Β¬ a0
t0: is_var (K1 t1 e) -> T0
i0: evalβ‚’ (a1 + a3 Ctx.top β‹… K1 t1 e) β‰Š ret_delay (i β‹… ORet)

Acc head_inst_nostep (Β¬ a0,' ORet)
a4, b0, a0: ty0
Ξ“: t_ctx
v: val Ξ“ a4
e0: ev_ctx Ξ“ b0
a1: (βˆ…β‚“ β–Άβ‚“ + (a4 β†’ b0)) =[ val_m ]> Ξ“
i: cvar Ξ“ Β¬ a0
t0: is_var (K2 v e0) -> T0
i0: evalβ‚’ (a1 + (a4 β†’ b0) Ctx.top β‹… K2 v e0) β‰Š ret_delay (i β‹… ORet)
Acc head_inst_nostep (Β¬ a0,' ORet)
a, a1, b: ty0
Ξ“: t_ctx
v: ev_ctx Ξ“ a
a0: (βˆ…β‚“ β–Άβ‚“ + a) =[ val_m ]> Ξ“
i: cvar Ξ“ + (a1 β†’ b)
t0: is_var v -> T0
i0: evalβ‚’ (a0 + a Ctx.top β‹… v) β‰Š ret_delay (i β‹… OApp)
Acc head_inst_nostep (+ (a1 β†’ b),' OApp)
a3, a0: ty0
Ξ“: t_ctx
b: ty0
t1: term Ξ“ (a3 β†’ b)
e: ev_ctx Ξ“ b
a1: (βˆ…β‚“ β–Άβ‚“ + a3) =[ val_m ]> Ξ“
i: cvar Ξ“ Β¬ a0
t0: is_var (K1 t1 e) -> T0
i0: evalβ‚’ (a1 + a3 Ctx.top β‹… K1 t1 e) β‰Š ret_delay (i β‹… ORet)

Acc head_inst_nostep (Β¬ a0,' ORet)
apply it_eq_step in i0; now inversion i0.
a4, b0, a0: ty0
Ξ“: t_ctx
v: val Ξ“ a4
e0: ev_ctx Ξ“ b0
a1: (βˆ…β‚“ β–Άβ‚“ + (a4 β†’ b0)) =[ val_m ]> Ξ“
i: cvar Ξ“ Β¬ a0
t0: is_var (K2 v e0) -> T0
i0: evalβ‚’ (a1 + (a4 β†’ b0) Ctx.top β‹… K2 v e0) β‰Š ret_delay (i β‹… ORet)

Acc head_inst_nostep (Β¬ a0,' ORet)
a4, b0, a0: ty0
Ξ“: t_ctx
v: val Ξ“ a4
e0: ev_ctx Ξ“ b0
i: cvar Ξ“ Β¬ a0
t0: is_var (K2 v e0) -> T0
vv: val_m Ξ“ + (a4 β†’ b0)
i0: evalβ‚’ (vv β‹… K2 v e0) β‰Š ret_delay (i β‹… ORet)

Acc head_inst_nostep (Β¬ a0,' ORet)
a4, b0, a0: ty0
Ξ“: t_ctx
v: val Ξ“ a4
e0: ev_ctx Ξ“ b0
i: cvar Ξ“ Β¬ a0
t0: is_var (K2 v e0) -> T0
c: Ξ“ βˆ‹ + (a4 β†’ b0)
r_rel: eqα΅’ (fun _ : T1 => obs_op βˆ™ Ξ“) T1_0 (c β‹… OApp ⦇ (! ▢ₐ v) ▢ₐ e0 ⦈) (i β‹… ORet)

Acc head_inst_nostep (Β¬ a0,' ORet)
inversion r_rel.
a, a1, b: ty0
Ξ“: t_ctx
v: ev_ctx Ξ“ a
a0: (βˆ…β‚“ β–Άβ‚“ + a) =[ val_m ]> Ξ“
i: cvar Ξ“ + (a1 β†’ b)
t0: is_var v -> T0
i0: evalβ‚’ (a0 + a Ctx.top β‹… v) β‰Š ret_delay (i β‹… OApp)

Acc head_inst_nostep (+ (a1 β†’ b),' OApp)
a, a1, b: ty0
Ξ“: t_ctx
v: ev_ctx Ξ“ a
a0: (βˆ…β‚“ β–Άβ‚“ + a) =[ val_m ]> Ξ“
i: cvar Ξ“ + (a1 β†’ b)
t0: is_var v -> T0
i0: evalβ‚’ (a0 + a Ctx.top β‹… v) β‰Š ret_delay (i β‹… OApp)
z: ty
p: obs z
H: head_inst_nostep (z,' p) (+ (a1 β†’ b),' OApp)

Acc head_inst_nostep (z,' p)
a, a1, b: ty0
Ξ“: t_ctx
v: ev_ctx Ξ“ a
a0: (βˆ…β‚“ β–Άβ‚“ + a) =[ val_m ]> Ξ“
i: cvar Ξ“ + (a1 β†’ b)
t0: is_var v -> T0
i0: evalβ‚’ (a0 + a Ctx.top β‹… v) β‰Š ret_delay (i β‹… OApp)
a2: ty0
Ξ“0: t_ctx
v0: val_m Ξ“0 + (a1 β†’ b)
a3: dom OApp =[ val_m ]> Ξ“0
i1: Ξ“0 βˆ‹ projT1 (Β¬ a2,' ORet)
t1: is_var v0 -> T0
i2: evalβ‚’ (v0 βŠ™ OApp β¦— a3 ⦘) β‰Š ret_delay (i1 β‹… projT2 (Β¬ a2,' ORet))

Acc head_inst_nostep (Β¬ a2,' ORet)
a, a1, b: ty0
Ξ“: t_ctx
v: ev_ctx Ξ“ a
a0: (βˆ…β‚“ β–Άβ‚“ + a) =[ val_m ]> Ξ“
i: cvar Ξ“ + (a1 β†’ b)
t0: is_var v -> T0
i0: evalβ‚’ (a0 + a Ctx.top β‹… v) β‰Š ret_delay (i β‹… OApp)
a3, b0: ty0
Ξ“0: t_ctx
v0: val_m Ξ“0 + (a1 β†’ b)
a2: dom OApp =[ val_m ]> Ξ“0
i1: Ξ“0 βˆ‹ projT1 (+ (a3 β†’ b0),' OApp)
t1: is_var v0 -> T0
i2: evalβ‚’ (v0 βŠ™ OApp β¦— a2 ⦘) β‰Š ret_delay (i1 β‹… projT2 (+ (a3 β†’ b0),' OApp))
Acc head_inst_nostep (+ (a3 β†’ b0),' OApp)
a, a5, b0: ty0
Ξ“: t_ctx
v: ev_ctx Ξ“ a
a0: (βˆ…β‚“ β–Άβ‚“ + a) =[ val_m ]> Ξ“
i: cvar Ξ“ + (a5 β†’ b0)
t0: is_var v -> T0
i0: evalβ‚’ (a0 + a Ctx.top β‹… v) β‰Š ret_delay (i β‹… OApp)
a2: ty0
Ξ“0: t_ctx
t2: term (Ξ“0 β–Άβ‚“ + (a5 β†’ b0) β–Άβ‚“ + a5) b0
a3: dom OApp =[ val_m ]> Ξ“0
i1: Ξ“0 βˆ‹ projT1 (Β¬ a2,' ORet)
t1: is_var (Lam t2) -> T0
i2: evalβ‚’ (Lam t2 βŠ™ OApp β¦— a3 ⦘) β‰Š ret_delay (i1 β‹… projT2 (Β¬ a2,' ORet))

Acc head_inst_nostep (Β¬ a2,' ORet)
a, a5, b1: ty0
Ξ“: t_ctx
v: ev_ctx Ξ“ a
a0: (βˆ…β‚“ β–Άβ‚“ + a) =[ val_m ]> Ξ“
i: cvar Ξ“ + (a5 β†’ b1)
t0: is_var v -> T0
i0: evalβ‚’ (a0 + a Ctx.top β‹… v) β‰Š ret_delay (i β‹… OApp)
a3, b0: ty0
Ξ“0: t_ctx
t2: term (Ξ“0 β–Άβ‚“ + (a5 β†’ b1) β–Άβ‚“ + a5) b1
a2: dom OApp =[ val_m ]> Ξ“0
i1: Ξ“0 βˆ‹ projT1 (+ (a3 β†’ b0),' OApp)
t1: is_var (Lam t2) -> T0
i2: evalβ‚’ (Lam t2 βŠ™ OApp β¦— a2 ⦘) β‰Š ret_delay (i1 β‹… projT2 (+ (a3 β†’ b0),' OApp))
Acc head_inst_nostep (+ (a3 β†’ b0),' OApp)
all: apply it_eq_step in i2; now inversion i2. Qed.

At this point we have finished all the hard work! We already enjoy the generic correctness theorem but don't know it yet! Lets define some shorthands for some generic notions applied to our case, to make it a welcoming nest.

The whole semantic is parametrized by typing scope Ξ” of "final channels". Typically this can be instanciated with the singleton [ Β¬ ans ] for some chosen type ans, which will correspond with the outside type of the testing-contexts from CIU-equivalence. Usually this answer type is taken among the positive (or shareable) types of our language, but in fact using our observation machinery we can project the value of any type onto its "shareable part". This is why our generic proof abstracts over this answer type and even allows several of them at the same time (that is, Ξ”). In our case, as all types in our language are unshareable, the positive part of any value is pretty useless: it is always a singleton. Yet our notion of testing still distinguishes terminating from non-terminating programs.

As discussed in the paper, the "native output" of the generic theorem is correctness with respect to an equivalence we call "substitution equivalence". We will recover a more standard CIU later on.

Definition subst_eq Ξ” {Ξ“} : relation (state Ξ“) :=
  fun u v => forall Οƒ : Ξ“ =[val_m]> Ξ”, evalβ‚’ (u β‚œβŠ› Οƒ) β‰ˆ evalβ‚’ (v β‚œβŠ› Οƒ) .
Notation "x β‰ˆS[ Ξ” ]β‰ˆ y" := (subst_eq Ξ” x y) (at level 10).

Our semantic objects live in what is defined in the generic construction as ogs_act, that is active strategies for the OGS game. They come with their own notion of equivalence, weak bisimilarity and we get to interpret states into semantic objects.

Definition sem_act Ξ” Ξ“ := ogs_act (obs := obs_op) Ξ” (βˆ… β–Άβ‚“ Ξ“) .

Definition ogs_weq_act Ξ” {Ξ“} : relation (sem_act Ξ” Ξ“) := fun u v => u β‰ˆ v .
Notation "u β‰ˆ[ Ξ” ]β‰ˆ v" := (ogs_weq_act Ξ” u v) (at level 40).

Definition interp_act_s Ξ” {Ξ“} (c : state Ξ“) : sem_act Ξ” Ξ“ :=
  m_strat (βˆ… β–Άβ‚“ Ξ“) (inj_init_act Ξ” c) .
Notation "OGS⟦ t ⟧" := (interp_act_s _ t) .

We can now obtain our instance of the correctness result!

Ξ”, Ξ“: t_ctx
x, y: state Ξ“

OGS⟦ x ⟧ β‰ˆ[ Ξ” ]β‰ˆ OGS⟦ y ⟧ -> x β‰ˆS[ Ξ” ]β‰ˆ y
exact (ogs_correction Ξ” x y). Qed.

Recovering CIU-equivalence

CIU-equivalence more usually defined as a relation on terms (and not some states), and involves an evaluation context. In our formalism it amounts to the following definition.

Definition ciu_eq Ξ” {Ξ“ a} : relation (term Ξ“ a) :=
  fun u v => forall (Οƒ : Ξ“ =[val_m]> Ξ”) (Ο€ : ev_ctx Ξ” a),
      evalβ‚’ ((u `β‚œβŠ› Οƒ) β‹… Ο€) β‰ˆ evalβ‚’ ((v `β‚œβŠ› Οƒ) β‹… Ο€) .
Notation "x β‰ˆC[ Ξ” ]β‰ˆ y" := (ciu_eq Ξ” x y) (at level 10).

Now from a term we can always construct a state by naming it, that is, placing the term opposite of a fresh context variable.

Definition c_init {Ξ“ a} (t : term Ξ“ a) : state (Ξ“ β–Άβ‚“ Β¬ a)
  := t_shift _ t β‹… K0 Ctx.top .
Notation "T⟦ t ⟧" := (OGS⟦ c_init t ⟧) .

Similarly, from an evaluation context and a substitution, we can form an extended substitution. Without surprise these two constructions simplify well in terms of substitution.

Definition a_of_sk {Ξ“ Ξ” a} (Οƒ : Ξ“ =[val_m]> Ξ”) (Ο€ : ev_ctx Ξ” a)
  : (Ξ“ β–Άβ‚“ Β¬ a) =[val_m]> Ξ” := Οƒ ▢ₐ (Ο€ : val_m _ (Β¬ _)) .

Ξ“, Ξ”: t_ctx
a: ty0
t: term Ξ“ a
Οƒ: Ξ“ =[ val_m ]> Ξ”
Ο€: ev_ctx Ξ” a

t `β‚œβŠ› Οƒ β‹… Ο€ = c_init t β‚œβŠ› a_of_sk Οƒ Ο€
cbn; unfold t_shift; 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!

Ξ”, Ξ“: t_ctx
a: ty0
x, y: term Ξ“ a

T⟦ x ⟧ β‰ˆ[ Ξ” ]β‰ˆ T⟦ y ⟧ -> x β‰ˆC[ Ξ” ]β‰ˆ y
Ξ”, Ξ“: t_ctx
a: ty0
x, y: term Ξ“ a
H: T⟦ x ⟧ β‰ˆ[ Ξ” ]β‰ˆ T⟦ y ⟧
Οƒ: Ξ“ =[ val_m ]> Ξ”
k: ev_ctx Ξ” a

evalβ‚’ (c_init x β‚œβŠ› a_of_sk Οƒ k) β‰ˆ evalβ‚’ (c_init y β‚œβŠ› a_of_sk Οƒ k)
now apply stlc_subst_correct. Qed.
[AACMM21]Guillaume Allais et al, "A type- and scope-safe universe of syntaxes with binding: their semantics and proofs", 2021.
[FS22]Marcelo Fiore & Dmitrij Szamozvancev, "Formal Metatheory of Second-Order Abstract Syntax", 2022.
[L05]Soren Lassen, "Eager Normal Form Bisimulation", 2005.