Require Import MyTactics. Require Import LambdaCalculusSyntax. Require Import LambdaCalculusValues. Require Import LambdaCalculusReduction. Require Import STLCDefinition.
The typing judgement is preserved by a renaming xi
that maps
term variables to term variables.
Note that xi
need not be injective or surjective.
When xi
is not surjective, we get a weakening lemma (see the next
lemma, below). When xi
is not injective, we obtain a contraction
lemma (not shown here).
forall (Gamma : tyenv) (t : term) (U : ty), jt Gamma t U -> forall (Gamma' : var -> ty) (xi : var -> var), Gamma = xi >>> Gamma' -> jt Gamma' t.[ren xi] U(* A detailed proof, where every case is dealt with explicitly: *)forall (Gamma : tyenv) (t : term) (U : ty), jt Gamma t U -> forall (Gamma' : var -> ty) (xi : var -> var), Gamma = xi >>> Gamma' -> jt Gamma' t.[ren xi] U(* JTVar *)x: var
Gamma': var -> ty
xi: var -> varjt Gamma' (Var x).[ren xi] ((xi >>> Gamma') x)t: term
T, U: ty
Gamma': var -> ty
xi: var -> var
IHjt: forall (Gamma'0 : var -> ty) (xi0 : var -> var), T .: xi >>> Gamma' = xi0 >>> Gamma'0 -> jt Gamma'0 t.[ren xi0] U
H: jt (T .: xi >>> Gamma') t Ujt Gamma' (Lam t).[ren xi] (TyFun T U)t1, t2: term
T, U: ty
Gamma': var -> ty
xi: var -> var
IHjt2: forall (Gamma'0 : var -> ty) (xi0 : var -> var), xi >>> Gamma' = xi0 >>> Gamma'0 -> jt Gamma'0 t2.[ren xi0] T
IHjt1: forall (Gamma'0 : var -> ty) (xi0 : var -> var), xi >>> Gamma' = xi0 >>> Gamma'0 -> jt Gamma'0 t1.[ren xi0] (TyFun T U)
H0: jt (xi >>> Gamma') t2 T
H: jt (xi >>> Gamma') t1 (TyFun T U)jt Gamma' (App t1 t2).[ren xi] Ux: var
Gamma': var -> ty
xi: var -> varjt Gamma' (Var x).[ren xi] ((xi >>> Gamma') x)x: var
Gamma': var -> ty
xi: var -> varjt Gamma' (ids (xi x)) (Gamma' (xi x))eauto.x: var
Gamma': var -> ty
xi: var -> varGamma' (xi x) = Gamma' (xi x)(* JTLam *)t: term
T, U: ty
Gamma': var -> ty
xi: var -> var
IHjt: forall (Gamma'0 : var -> ty) (xi0 : var -> var), T .: xi >>> Gamma' = xi0 >>> Gamma'0 -> jt Gamma'0 t.[ren xi0] U
H: jt (T .: xi >>> Gamma') t Ujt Gamma' (Lam t).[ren xi] (TyFun T U)t1, t2: term
T, U: ty
Gamma': var -> ty
xi: var -> var
IHjt2: forall (Gamma'0 : var -> ty) (xi0 : var -> var), xi >>> Gamma' = xi0 >>> Gamma'0 -> jt Gamma'0 t2.[ren xi0] T
IHjt1: forall (Gamma'0 : var -> ty) (xi0 : var -> var), xi >>> Gamma' = xi0 >>> Gamma'0 -> jt Gamma'0 t1.[ren xi0] (TyFun T U)
H0: jt (xi >>> Gamma') t2 T
H: jt (xi >>> Gamma') t1 (TyFun T U)jt Gamma' (App t1 t2).[ren xi] Ut: term
T, U: ty
Gamma': var -> ty
xi: var -> var
IHjt: forall (Gamma'0 : var -> ty) (xi0 : var -> var), T .: xi >>> Gamma' = xi0 >>> Gamma'0 -> jt Gamma'0 t.[ren xi0] U
H: jt (T .: xi >>> Gamma') t Ujt Gamma' (Lam t).[ren xi] (TyFun T U)t: term
T, U: ty
Gamma': var -> ty
xi: var -> var
IHjt: forall (Gamma'0 : var -> ty) (xi0 : var -> var), T .: xi >>> Gamma' = xi0 >>> Gamma'0 -> jt Gamma'0 t.[ren xi0] U
H: jt (T .: xi >>> Gamma') t Ujt Gamma' (Lam t.[ren (upren xi)]) (TyFun T U)t: term
T, U: ty
Gamma': var -> ty
xi: var -> var
IHjt: forall (Gamma'0 : var -> ty) (xi0 : var -> var), T .: xi >>> Gamma' = xi0 >>> Gamma'0 -> jt Gamma'0 t.[ren xi0] U
H: jt (T .: xi >>> Gamma') t Ujt (T .: Gamma') t.[ren (upren xi)] Uautosubst.t: term
T, U: ty
Gamma': var -> ty
xi: var -> var
IHjt: forall (Gamma'0 : var -> ty) (xi0 : var -> var), T .: xi >>> Gamma' = xi0 >>> Gamma'0 -> jt Gamma'0 t.[ren xi0] U
H: jt (T .: xi >>> Gamma') t UT .: xi >>> Gamma' = upren xi >>> T .: Gamma'(* JTApp *)t1, t2: term
T, U: ty
Gamma': var -> ty
xi: var -> var
IHjt2: forall (Gamma'0 : var -> ty) (xi0 : var -> var), xi >>> Gamma' = xi0 >>> Gamma'0 -> jt Gamma'0 t2.[ren xi0] T
IHjt1: forall (Gamma'0 : var -> ty) (xi0 : var -> var), xi >>> Gamma' = xi0 >>> Gamma'0 -> jt Gamma'0 t1.[ren xi0] (TyFun T U)
H0: jt (xi >>> Gamma') t2 T
H: jt (xi >>> Gamma') t1 (TyFun T U)jt Gamma' (App t1 t2).[ren xi] Ut1, t2: term
T, U: ty
Gamma': var -> ty
xi: var -> var
IHjt2: forall (Gamma'0 : var -> ty) (xi0 : var -> var), xi >>> Gamma' = xi0 >>> Gamma'0 -> jt Gamma'0 t2.[ren xi0] T
IHjt1: forall (Gamma'0 : var -> ty) (xi0 : var -> var), xi >>> Gamma' = xi0 >>> Gamma'0 -> jt Gamma'0 t1.[ren xi0] (TyFun T U)
H0: jt (xi >>> Gamma') t2 T
H: jt (xi >>> Gamma') t1 (TyFun T U)jt Gamma' (App t1 t2).[ren xi] Ut1, t2: term
T, U: ty
Gamma': var -> ty
xi: var -> var
IHjt2: forall (Gamma'0 : var -> ty) (xi0 : var -> var), xi >>> Gamma' = xi0 >>> Gamma'0 -> jt Gamma'0 t2.[ren xi0] T
IHjt1: forall (Gamma'0 : var -> ty) (xi0 : var -> var), xi >>> Gamma' = xi0 >>> Gamma'0 -> jt Gamma'0 t1.[ren xi0] (TyFun T U)
H0: jt (xi >>> Gamma') t2 T
H: jt (xi >>> Gamma') t1 (TyFun T U)jt Gamma' (App t1.[ren xi] t2.[ren xi]) Ut1, t2: term
T, U: ty
Gamma': var -> ty
xi: var -> var
IHjt2: forall (Gamma'0 : var -> ty) (xi0 : var -> var), xi >>> Gamma' = xi0 >>> Gamma'0 -> jt Gamma'0 t2.[ren xi0] T
IHjt1: forall (Gamma'0 : var -> ty) (xi0 : var -> var), xi >>> Gamma' = xi0 >>> Gamma'0 -> jt Gamma'0 t1.[ren xi0] (TyFun T U)
H0: jt (xi >>> Gamma') t2 T
H: jt (xi >>> Gamma') t1 (TyFun T U)jt Gamma' t1.[ren xi] (TyFun ?T U)t1, t2: term
T, U: ty
Gamma': var -> ty
xi: var -> var
IHjt2: forall (Gamma'0 : var -> ty) (xi0 : var -> var), xi >>> Gamma' = xi0 >>> Gamma'0 -> jt Gamma'0 t2.[ren xi0] T
IHjt1: forall (Gamma'0 : var -> ty) (xi0 : var -> var), xi >>> Gamma' = xi0 >>> Gamma'0 -> jt Gamma'0 t1.[ren xi0] (TyFun T U)
H0: jt (xi >>> Gamma') t2 T
H: jt (xi >>> Gamma') t1 (TyFun T U)jt Gamma' t2.[ren xi] ?Teauto. }t1, t2: term
T, U: ty
Gamma': var -> ty
xi: var -> var
IHjt2: forall (Gamma'0 : var -> ty) (xi0 : var -> var), xi >>> Gamma' = xi0 >>> Gamma'0 -> jt Gamma'0 t2.[ren xi0] T
IHjt1: forall (Gamma'0 : var -> ty) (xi0 : var -> var), xi >>> Gamma' = xi0 >>> Gamma'0 -> jt Gamma'0 t1.[ren xi0] (TyFun T U)
H0: jt (xi >>> Gamma') t2 T
H: jt (xi >>> Gamma') t1 (TyFun T U)jt Gamma' t2.[ren xi] T(* A shorter script, where all cases are dealt with uniformly: *) induction 1; intros; subst; asimpl; econstructor; eauto with autosubst. Qed.forall (Gamma : tyenv) (t : term) (U : ty), jt Gamma t U -> forall (Gamma' : var -> ty) (xi : var -> var), Gamma = xi >>> Gamma' -> jt Gamma' t.[ren xi] U
As a corollary, jt
is preserved by the renaming (+1)
.
This lemma is usually known as a weakening lemma, where the word "weakening" is used in the sense of linear logic. On paper, in nominal style (as opposed to de Bruijn style), one would write: if the typing judgement Γ ⊢ t : U holds, and if the variable x does not occur in t, then the typing judgement Γ; x : T ⊢ t : U holds.
Here, the variable x is represented by the de Bruijn index 0.
forall (Gamma : tyenv) (t : term) (T U : ty), jt Gamma t U -> jt (T .: Gamma) (lift 1 t) Uforall (Gamma : tyenv) (t : term) (T U : ty), jt Gamma t U -> jt (T .: Gamma) (lift 1 t) UGamma: tyenv
t: term
T, U: ty
H: jt Gamma t Ujt (T .: Gamma) (lift 1 t) UGamma: tyenv
t: term
T, U: ty
H: jt Gamma t Ujt ?Gamma t UGamma: tyenv
t: term
T, U: ty
H: jt Gamma t U?Gamma = (+1) >>> T .: Gammaautosubst. Qed.Gamma: tyenv
t: term
T, U: ty
H: jt Gamma t UGamma = (+1) >>> T .: Gamma
The typing judgement is extended to substitutions of terms for term variables.
With respect to a type environment Gamma
, a substitution sigma
has
type Delta
if and only if, for every variable x
, the term sigma
x
has type Delta x
.
This auxiliary judgement encourages us to think of terms of total substitutions, where every variable is replaced by a term. This concept turns out to be easier to understand and manipulate, especially during proofs by induction. Of course one always later consider the special case of a substitution that seems to affect just one variable, say variable 0. (In reality, such a substitution affects all variables, as the variables other than 0 are renumbered.)
Definition js Gamma sigma Delta :=
forall x : var,
jt Gamma (sigma x) (Delta x).
The following are basic lemmas about js
.
The identity substitution has type Gamma
in environment Gamma
.
forall Gamma : tyenv, js Gamma ids Gammaforall Gamma : tyenv, js Gamma ids Gammaeauto with jt. Qed.forall (Gamma : tyenv) (x : var), jt Gamma (ids x) (Gamma x)
The typing judgement js
behaves like an infinite list of typing judgements
jt
. So, one can prepend one more jt
judgement in front of it.
forall (Gamma : tyenv) (t : term) (sigma : var -> term) (T : ty) (Delta : var -> ty), jt Gamma t T -> js Gamma sigma Delta -> js Gamma (t .: sigma) (T .: Delta)forall (Gamma : tyenv) (t : term) (sigma : var -> term) (T : ty) (Delta : var -> ty), jt Gamma t T -> js Gamma sigma Delta -> js Gamma (t .: sigma) (T .: Delta)intros [|x]; asimpl; eauto. Qed.Gamma: tyenv
t: term
sigma: var -> term
T: ty
Delta: var -> ty
H: jt Gamma t T
H0: js Gamma sigma Deltajs Gamma (t .: sigma) (T .: Delta)
The typing judgement js
is preserved by the introduction of a new term
variable. That is, a typing judgement js
can be pushed under a
lambda-abstraction.
forall (Gamma : tyenv) (sigma : var -> term) (Delta : var -> ty) (T : ty), js Gamma sigma Delta -> js (T .: Gamma) (up sigma) (T .: Delta)forall (Gamma : tyenv) (sigma : var -> term) (Delta : var -> ty) (T : ty), js Gamma sigma Delta -> js (T .: Gamma) (up sigma) (T .: Delta)Gamma: tyenv
sigma: var -> term
Delta: var -> ty
T: ty
H: js Gamma sigma Deltajs (T .: Gamma) (up sigma) (T .: Delta)Gamma: tyenv
sigma: var -> term
Delta: var -> ty
T: ty
H: js Gamma sigma Deltajt (T .: Gamma) (ids 0) TGamma: tyenv
sigma: var -> term
Delta: var -> ty
T: ty
H: js Gamma sigma Deltajs (T .: Gamma) (sigma >>> rename (+1)) Deltaeauto with jt.Gamma: tyenv
sigma: var -> term
Delta: var -> ty
T: ty
H: js Gamma sigma Deltajt (T .: Gamma) (ids 0) TGamma: tyenv
sigma: var -> term
Delta: var -> ty
T: ty
H: js Gamma sigma Deltajs (T .: Gamma) (sigma >>> rename (+1)) DeltaGamma: tyenv
sigma: var -> term
Delta: var -> ty
T: ty
H: js Gamma sigma Deltajs (T .: Gamma) (sigma >>> rename (+1)) DeltaGamma: tyenv
sigma: var -> term
Delta: var -> ty
T: ty
H: js Gamma sigma Delta
x: varjt (T .: Gamma) ((sigma >>> rename (+1)) x) (Delta x)eauto using jt_te_renaming_0. } Qed.Gamma: tyenv
sigma: var -> term
Delta: var -> ty
T: ty
H: js Gamma sigma Delta
x: varjt (T .: Gamma) (lift 1 (sigma x)) (Delta x)
The typing judgement is preserved by a well-typed substitution sigma
of (all) term variables to terms.
forall (Delta : tyenv) (t : term) (U : ty), jt Delta t U -> forall (Gamma : tyenv) (sigma : var -> term), js Gamma sigma Delta -> jt Gamma t.[sigma] U(* A short script, where all cases are dealt with in the same way: *) induction 1; intros; subst; asimpl; eauto using js_up with jt. Qed.forall (Delta : tyenv) (t : term) (U : ty), jt Delta t U -> forall (Gamma : tyenv) (sigma : var -> term), js Gamma sigma Delta -> jt Gamma t.[sigma] U
As a corollary, the typing judgement is preserved by a well-typed substitution of one term for one variable, namely variable 0.
This property is exploited in the proof of subject reduction, in the case of beta-reduction.
forall (Gamma : var -> ty) (t1 t2 : term) (T U : ty), jt (T .: Gamma) t1 U -> jt Gamma t2 T -> jt Gamma t1.[t2/] U(* One can do the proof step by step as follows: *)forall (Gamma : var -> ty) (t1 t2 : term) (T U : ty), jt (T .: Gamma) t1 U -> jt Gamma t2 T -> jt Gamma t1.[t2/] UGamma: var -> ty
t1, t2: term
T, U: ty
H: jt (T .: Gamma) t1 U
H0: jt Gamma t2 Tjt Gamma t1.[t2/] UGamma: var -> ty
t1, t2: term
T, U: ty
H: jt (T .: Gamma) t1 U
H0: jt Gamma t2 Tjt ?Delta t1 UGamma: var -> ty
t1, t2: term
T, U: ty
H: jt (T .: Gamma) t1 U
H0: jt Gamma t2 Tjs Gamma (t2 .: ids) ?Deltaeauto.Gamma: var -> ty
t1, t2: term
T, U: ty
H: jt (T .: Gamma) t1 U
H0: jt Gamma t2 Tjt ?Delta t1 UGamma: var -> ty
t1, t2: term
T, U: ty
H: jt (T .: Gamma) t1 U
H0: jt Gamma t2 Tjs Gamma (t2 .: ids) (T .: Gamma)Gamma: var -> ty
t1, t2: term
T, U: ty
H: jt (T .: Gamma) t1 U
H0: jt Gamma t2 Tjs Gamma (t2 .: ids) (T .: Gamma)Gamma: var -> ty
t1, t2: term
T, U: ty
H: jt (T .: Gamma) t1 U
H0: jt Gamma t2 Tjt Gamma t2 TGamma: var -> ty
t1, t2: term
T, U: ty
H: jt (T .: Gamma) t1 U
H0: jt Gamma t2 Tjs Gamma ids Gammaeauto.Gamma: var -> ty
t1, t2: term
T, U: ty
H: jt (T .: Gamma) t1 U
H0: jt Gamma t2 Tjt Gamma t2 TGamma: var -> ty
t1, t2: term
T, U: ty
H: jt (T .: Gamma) t1 U
H0: jt Gamma t2 Tjs Gamma ids Gammaeapply js_ids. } } (* Of course one can also let Coq find the proof by itself: *)Gamma: var -> ty
t1, t2: term
T, U: ty
H: jt (T .: Gamma) t1 U
H0: jt Gamma t2 Tjs Gamma ids Gammaeauto using jt_te_substitution, js_ids, js_cons. Qed.forall (Gamma : var -> ty) (t1 t2 : term) (T U : ty), jt (T .: Gamma) t1 U -> jt Gamma t2 T -> jt Gamma t1.[t2/] U