Library HOAS_Object_Adequacy
Adequacy of the collapsed presentation with only a single binding form
with respect to our original locally nameless presentation.
Author: Brian Aydemir (baydemir
Author: Brian Aydemir (baydemir
at
cis.upenn.edu)
Require Export Omega.
Require Export Fsub_LetSum_Soundness.
Require Export HOAS_Object_Soundness.
Because both developments use the same names, we use the module
system to provide convenient shorthand prefixes that enable us to
distinguish symbols. The downside to doing this is that it seems
to interfere with Coq's tactic automation, in ways I do not
entirely understand.
Module A := Fsub_LetSum_Definitions.
Module A' := Fsub_LetSum_Infrastructure.
Module A'' := Fsub_LetSum_Soundness.
Module B := HOAS_Object_Definitions.
Module B' := HOAS_Object_Infrastructure.
Module B'' := HOAS_Object_Soundness.
We redefine some tactics so that they are effective in our current
setting.
Ltac gather_atoms :=
let A := gather_atoms_with (fun x : atoms => x) in
let B := gather_atoms_with (fun x : atom => singleton x) in
let C := gather_atoms_with (fun x : A.exp => A.fv_te x) in
let D := gather_atoms_with (fun x : A.exp => A.fv_ee x) in
let E := gather_atoms_with (fun x : A.typ => A.fv_tt x) in
let F := gather_atoms_with (fun x : A.senv => dom x) in
let G := gather_atoms_with (fun x : A.env => dom x) in
let H := gather_atoms_with (fun x : B.senv => dom x) in
let J := gather_atoms_with (fun x : B.env => dom x) in
let K := gather_atoms_with (fun x : B.syntax => B.fv x) in
constr:(A `union` B `union` C `union` D `union`
E `union` F `union` G `union` H `union` J `union` K).
Tactic Notation "pick" "fresh" ident(x) :=
let L := gather_atoms in (pick fresh x for L).
Tactic Notation
"pick" "fresh" ident(atom_name) "and" "apply" constr(lemma) :=
let L := gather_atoms in
pick fresh atom_name excluding L and apply lemma.
Opening a term with a variable is an injective operation if the
variable is sufficiently fresh.
Lemma open_tt_injective_rec : forall T S k (X : atom),
X `notin` (A.fv_tt T `union` A.fv_tt S) ->
A.open_tt_rec k X T = A.open_tt_rec k X S ->
T = S.
Lemma open_tt_injective : forall T S (X : atom),
X `notin` (A.fv_tt T `union` A.fv_tt S) ->
A.open_tt T X = A.open_tt S X ->
T = S.
Lemma open_te_injective_rec : forall e f k (X : atom),
X `notin` (A.fv_te e `union` A.fv_te f) ->
A.open_te_rec k X e = A.open_te_rec k X f ->
e = f.
Lemma open_te_injective : forall e f (X : atom),
X `notin` (A.fv_te e `union` A.fv_te f) ->
A.open_te e X = A.open_te f X ->
e = f.
Lemma open_ee_injective_rec : forall e f k (X : atom),
X `notin` (A.fv_ee e `union` A.fv_ee f) ->
A.open_ee_rec k X e = A.open_ee_rec k X f ->
e = f.
Lemma open_ee_injective : forall e f (X : atom),
X `notin` (A.fv_ee e `union` A.fv_ee f) ->
A.open_ee e X = A.open_ee f X ->
e = f.
Lemma open_injective_rec : forall T S k (X : atom),
X `notin` (B.fv T `union` B.fv S) ->
B.open_rec k X T = B.open_rec k X S ->
T = S.
Lemma open_injective : forall T S (X : atom),
X `notin` (B.fv T `union` B.fv S) ->
B.open T X = B.open S X ->
T = S.
A term is at level
n
if the greatest index in the term is
strictly less than n
.
Inductive level_t : nat -> A.typ -> Prop :=
| level_t_top : forall n,
level_t n A.typ_top
| level_t_bvar : forall n i,
i < n ->
level_t n (A.typ_bvar i)
| level_t_fvar : forall n X,
level_t n (A.typ_fvar X)
| level_t_arrow : forall n T1 T2,
level_t n T1 ->
level_t n T2 ->
level_t n (A.typ_arrow T1 T2)
| level_t_all : forall n T1 T2,
level_t n T1 ->
level_t (S n) T2 ->
level_t n (A.typ_all T1 T2)
| level_t_sum : forall n T1 T2,
level_t n T1 ->
level_t n T2 ->
level_t n (A.typ_sum T1 T2).
Hint Constructors level_t.
Inductive level_e : nat -> A.exp -> Prop :=
| level_e_bvar : forall n i,
i < n ->
level_e n (A.exp_bvar i)
| level_e_fvar : forall n X,
level_e n (A.exp_fvar X)
| level_e_abs : forall n T e,
level_t n T ->
level_e (S n) e ->
level_e n (A.exp_abs T e)
| level_e_app : forall n e1 e2,
level_e n e1 ->
level_e n e2 ->
level_e n (A.exp_app e1 e2)
| level_e_tabs : forall n T e,
level_t n T ->
level_e (S n) e ->
level_e n (A.exp_tabs T e)
| level_e_tapp : forall n e1 T2,
level_e n e1 ->
level_t n T2 ->
level_e n (A.exp_tapp e1 T2)
| level_e_let : forall n e1 e2,
level_e n e1 ->
level_e (S n) e2 ->
level_e n (A.exp_let e1 e2)
| level_e_inl : forall n e1,
level_e n e1 ->
level_e n (A.exp_inl e1)
| level_e_inr : forall n e1,
level_e n e1 ->
level_e n (A.exp_inr e1)
| level_e_case : forall n e1 e2 e3,
level_e n e1 ->
level_e (S n) e2 ->
level_e (S n) e3 ->
level_e n (A.exp_case e1 e2 e3).
Hint Constructors level_e.
Inductive level : nat -> B.syntax -> Prop :=
| level_bvar : forall n i,
i < n ->
level n (B.bvar i)
| level_fvar : forall n X,
level n (B.fvar X)
| level_habs : forall n e,
level (S n) e ->
level n (B.abs e)
| level_top : forall n,
level n B.typ_top
| level_arrow : forall n T1 T2,
level n T1 ->
level n T2 ->
level n (B.typ_arrow T1 T2)
| level_all : forall n T1 T2,
level n T1 ->
level n T2 ->
level n (B.typ_all T1 T2)
| level_sum : forall n T1 T2,
level n T1 ->
level n T2 ->
level n (B.typ_sum T1 T2)
| level_abs : forall n T e,
level n T ->
level n e ->
level n (B.exp_abs T e)
| level_app : forall n e1 e2,
level n e1 ->
level n e2 ->
level n (B.exp_app e1 e2)
| level_tabs : forall n T e,
level n T ->
level n e ->
level n (B.exp_tabs T e)
| level_tapp : forall n e1 T2,
level n e1 ->
level n T2 ->
level n (B.exp_tapp e1 T2)
| level_let : forall n e1 e2,
level n e1 ->
level n e2 ->
level n (B.exp_let e1 e2)
| level_inl : forall n e1,
level n e1 ->
level n (B.exp_inl e1)
| level_inr : forall n e1,
level n e1 ->
level n (B.exp_inr e1)
| level_case : forall n e1 e2 e3,
level n e1 ->
level n e2 ->
level n e3 ->
level n (B.exp_case e1 e2 e3).
Hint Constructors level.
A term at level
n
is also a term at level (S n)
.
Lemma level_t_promote : forall n T,
level_t n T ->
level_t (S n) T.
Hint Resolve level_t_promote.
Lemma level_e_promote : forall n e,
level_e n e ->
level_e (S n) e.
Hint Resolve level_e_promote.
The following lemmas establish the relationship between the level
of a term and opening.
Lemma level_t_open_tt : forall T2 n (X : atom),
level_t n (A.open_tt_rec n X T2) ->
level_t (S n) T2.
Lemma level_e_open_te : forall e n (x : atom),
level_e n (A.open_te_rec n x e) ->
level_e (S n) e.
Lemma level_e_open_ee : forall e n (x : atom),
level_e n (A.open_ee_rec n x e) ->
level_e (S n) e.
Lemma level_open : forall T n (X : atom),
level n (B.open_rec n X T) ->
level (S n) T.
Locally closed terms are at level 0.
Lemma level_t_of_type : forall T,
A.type T -> level_t 0 T.
Hint Resolve level_t_of_type.
Lemma level_e_of_expr : forall e,
A.expr e -> level_e 0 e.
Hint Resolve level_e_of_expr.
Lemma level_of_lc : forall T,
B.lc T -> level 0 T.
Hint Resolve level_of_lc.
Closing replaces a free variable with an index. The definition
below assumes that
K
is greater than the greatest index
appearing in T
.
Fixpoint close_tt_rec (K : nat) (X : atom) (T : A.typ) {struct T} : A.typ :=
match T with
| A.typ_top => A.typ_top
| A.typ_bvar n => A.typ_bvar n
| A.typ_fvar Y => if X == Y then (A.typ_bvar K) else (A.typ_fvar Y)
| A.typ_arrow T1 T2 =>
A.typ_arrow (close_tt_rec K X T1) (close_tt_rec K X T2)
| A.typ_all T1 T2 =>
A.typ_all (close_tt_rec K X T1) (close_tt_rec (S K) X T2)
| A.typ_sum T1 T2 =>
A.typ_sum (close_tt_rec K X T1) (close_tt_rec K X T2)
end.
Definition close_tt T X := close_tt_rec 0 X T.
Fixpoint close_te_rec (k : nat) (X : atom) (e : A.exp) {struct e} : A.exp :=
match e with
| A.exp_bvar n => A.exp_bvar n
| A.exp_fvar x => A.exp_fvar x
| A.exp_abs T e =>
A.exp_abs (close_tt_rec k X T) (close_te_rec (S k) X e)
| A.exp_app e1 e2 =>
A.exp_app (close_te_rec k X e1) (close_te_rec k X e2)
| A.exp_tabs T e =>
A.exp_tabs (close_tt_rec k X T) (close_te_rec (S k) X e)
| A.exp_tapp e1 T2 =>
A.exp_tapp (close_te_rec k X e1) (close_tt_rec k X T2)
| A.exp_let e1 e2 =>
A.exp_let (close_te_rec k X e1) (close_te_rec (S k) X e2)
| A.exp_inl e1 => A.exp_inl (close_te_rec k X e1)
| A.exp_inr e1 => A.exp_inr (close_te_rec k X e1)
| A.exp_case e1 e2 e3 =>
A.exp_case (close_te_rec k X e1)
(close_te_rec (S k) X e2)
(close_te_rec (S k) X e3)
end.
Definition close_te e X := close_te_rec 0 X e.
Fixpoint close_ee_rec (k : nat) (y : atom) (e : A.exp) {struct e} : A.exp :=
match e with
| A.exp_bvar n => A.exp_bvar n
| A.exp_fvar x => if y == x then (A.exp_bvar k) else (A.exp_fvar x)
| A.exp_abs T e =>
A.exp_abs T (close_ee_rec (S k) y e)
| A.exp_app e1 e2 =>
A.exp_app (close_ee_rec k y e1) (close_ee_rec k y e2)
| A.exp_tabs T e =>
A.exp_tabs T (close_ee_rec (S k) y e)
| A.exp_tapp e1 T2 =>
A.exp_tapp (close_ee_rec k y e1) T2
| A.exp_let e1 e2 =>
A.exp_let (close_ee_rec k y e1) (close_ee_rec (S k) y e2)
| A.exp_inl e1 => A.exp_inl (close_ee_rec k y e1)
| A.exp_inr e1 => A.exp_inr (close_ee_rec k y e1)
| A.exp_case e1 e2 e3 =>
A.exp_case (close_ee_rec k y e1)
(close_ee_rec (S k) y e2)
(close_ee_rec (S k) y e3)
end.
Definition close_ee e y := close_ee_rec 0 y e.
Fixpoint close_rec (K : nat) (X : atom) (t : B.syntax) {struct t} : B.syntax :=
match t with
| B.bvar n => B.bvar n
| B.fvar Y => if X == Y then (B.bvar K) else (B.fvar Y)
| B.abs e => B.abs (close_rec (S K) X e)
| B.typ_top => B.typ_top
| B.typ_arrow T1 T2 => B.typ_arrow (close_rec K X T1) (close_rec K X T2)
| B.typ_all T1 T2 => B.typ_all (close_rec K X T1) (close_rec K X T2)
| B.typ_sum T1 T2 => B.typ_sum (close_rec K X T1) (close_rec K X T2)
| B.exp_abs T e => B.exp_abs (close_rec K X T) (close_rec K X e)
| B.exp_app e1 e2 => B.exp_app (close_rec K X e1) (close_rec K X e2)
| B.exp_tabs T e => B.exp_tabs (close_rec K X T) (close_rec K X e)
| B.exp_tapp e T => B.exp_tapp (close_rec K X e) (close_rec K X T)
| B.exp_let e1 e2 => B.exp_let (close_rec K X e1) (close_rec K X e2)
| B.exp_inl e1 => B.exp_inl (close_rec K X e1)
| B.exp_inr e1 => B.exp_inr (close_rec K X e1)
| B.exp_case e1 e2 e3 =>
B.exp_case (close_rec K X e1)
(close_rec K X e2)
(close_rec K X e3)
end.
Definition close T X := close_rec 0 X T.
Opening and closing are inverses of each other under certain
conditions.
Lemma open_tt_close_tt_inv_rec : forall (T : A.typ) (K : nat) (X : atom),
level_t K T ->
A.open_tt_rec K (A.typ_fvar X) (close_tt_rec K X T) = T.
Lemma open_tt_close_tt_inv : forall (T : A.typ) (X : atom),
A.type T ->
A.open_tt (close_tt T X) X = T.
Lemma open_te_close_te_inv_rec : forall (e : A.exp) (K : nat) (X : atom),
level_e K e ->
A.open_te_rec K (A.typ_fvar X) (close_te_rec K X e) = e.
Lemma open_te_close_te_inv : forall (e : A.exp) (X : atom),
A.expr e ->
A.open_te (close_te e X) X = e.
Lemma open_ee_close_ee_inv_rec : forall (e : A.exp) (k : nat) (x : atom),
level_e k e ->
A.open_ee_rec k (A.exp_fvar x) (close_ee_rec k x e) = e.
Lemma open_ee_close_ee_inv : forall (e : A.exp) (x : atom),
A.expr e ->
A.open_ee (close_ee e x) x = e.
Lemma open_close_inv_rec : forall (T : B.syntax) (K : nat) (X : atom),
level K T ->
B.open_rec K (B.fvar X) (close_rec K X T) = T.
Lemma open_close_inv : forall (T : B.syntax) (X : atom),
B.lc T ->
B.open (close T X) X = T.
If we close a term with a particular name, that name will be fresh
for the result.
Lemma close_tt_fresh_rec : forall (T : A.typ) (K : nat) (X : atom),
X `notin` A.fv_tt (close_tt_rec K X T).
Hint Resolve close_tt_fresh_rec.
Lemma close_tt_fresh : forall (T : A.typ) (X : atom),
X `notin` A.fv_tt (close_tt T X).
Hint Resolve close_tt_fresh.
Lemma close_te_fresh_te_rec : forall e1 (X : atom) (k : nat),
X `notin` A.fv_te (close_te_rec k X e1).
Lemma close_te_fresh_te : forall e1 (X : atom),
X `notin` A.fv_te (close_te e1 X).
Hint Resolve close_te_fresh_te.
Lemma close_ee_fresh_ee_rec : forall e1 (x : atom) (k : nat),
x `notin` A.fv_ee (close_ee_rec k x e1).
Lemma close_ee_fresh_ee : forall e1 (x : atom),
x `notin` A.fv_ee (close_ee e1 x).
Hint Resolve close_ee_fresh_ee.
Lemma close_fresh_rec : forall (T : B.syntax) (K : nat) (X : atom),
X `notin` B.fv (close_rec K X T).
Hint Resolve close_fresh_rec.
Lemma close_fresh : forall (T : B.syntax) (X : atom),
X `notin` B.fv (close T X).
Hint Resolve close_fresh.
In general, two developments may use different environments (given
by the type
senv
in each development, according to our naming
convention) for checking the well-formedness of Fsub types and
expressions. In order to define the bijection between Fsub terms,
we need a bijection on senv
s.
The definition here is trivial because
A.senv
and B.senv
are
the same type. The definition specifically does not check that
the environments are ok; that must be done elsewhere.
Inductive senv_bijection : A.senv -> B.senv -> Prop :=
| senv_bijection_refl : forall E,
senv_bijection E E.
Hint Constructors senv_bijection.
We define a tactic for simplifying
senv_bijection
propositions
in the hypothesis list.
Ltac simplify_senv_bijection :=
match goal with
| H : senv_bijection ?E ?F |- _ =>
inversion H; try subst F; try subst; clear H; simplify_senv_bijection
| _ =>
idtac
end.
The bijection respects environment concatentation.
Lemma senv_bijection_app : forall E E' F F',
senv_bijection E E' ->
senv_bijection F F' ->
senv_bijection (E ++ F) (E' ++ F').
Hint Resolve senv_bijection_app.
Lemma senv_bijection_length : forall E E',
senv_bijection E E' ->
length E = length E'.
Lemma senv_bijection_head : forall E E' F F',
senv_bijection (F ++ E) (F' ++ E') ->
senv_bijection E E' ->
senv_bijection F F'.
We first define a relation on well-formed Fsub types and then show that
the relation defines a bijection on such terms.
Inductive typ_bijection : A.senv -> A.typ ->
B.senv -> B.syntax -> Prop :=
| typ_bijection_var : forall E E' X,
ok E ->
ok E' ->
binds X Typ E ->
binds X Typ E' ->
senv_bijection E E' ->
typ_bijection E (A.typ_fvar X) E' (B.fvar X)
| typ_bijection_top : forall E E',
ok E ->
ok E' ->
senv_bijection E E' ->
typ_bijection E (A.typ_top) E' (B.typ_top)
| typ_bijection_arrow : forall E E' T1 T1' T2 T2',
typ_bijection E T1 E' T1' ->
typ_bijection E T2 E' T2' ->
typ_bijection E (A.typ_arrow T1 T2) E' (B.typ_arrow T1' T2')
| typ_bijection_all : forall L E E' T1 T1' T2 T2',
typ_bijection E T1 E' T1' ->
(forall X : atom, X `notin` L ->
typ_bijection ([(X,Typ)] ++ E) (A.open_tt T2 X)
([(X,Typ)] ++ E') (B.open T2' X)) ->
typ_bijection E (A.typ_all T1 T2) E' (B.typ_all T1' (B.abs T2'))
| typ_bijection_sum : forall E E' T1 T1' T2 T2',
typ_bijection E T1 E' T1' ->
typ_bijection E T2 E' T2' ->
typ_bijection E (A.typ_sum T1 T2) E' (B.typ_sum T1' T2').
Hint Constructors typ_bijection.
The relation contains only related environments.
Lemma typ_bijection_senv : forall E E' T1 T2,
typ_bijection E T1 E' T2 ->
senv_bijection E E'.
Hint Resolve typ_bijection_senv.
The relation contains only well-formed types.
Lemma typ_bijection_regular_1 : forall E E' T1 T2,
typ_bijection E T1 E' T2 ->
A.wf_typ E T1.
Hint Resolve typ_bijection_regular_1.
Lemma typ_bijection_regular_2 : forall E E' T1 T2,
typ_bijection E T1 E' T2 ->
B.wf_typ E' T2.
Hint Resolve typ_bijection_regular_2.
The bijection only holds for ok environments.
Lemma typ_bijection_ok_1 : forall E T1 E' T2,
typ_bijection E T1 E' T2 ->
ok E.
Hint Resolve typ_bijection_ok_1.
Lemma typ_bijection_ok_2 : forall E E' T1 T2,
typ_bijection E T1 E' T2 ->
ok E'.
Hint Resolve typ_bijection_ok_2.
Define a tactic to simplify proving well-formedness goals.
Ltac solve_lc_1 := try first [
solve [apply lc_var]
| solve [apply type_var]
| solve [eapply B''.type_from_wf_typ; eapply typ_bijection_regular_2; eauto]
| solve [eapply A''.type_from_wf_typ; eapply typ_bijection_regular_1; eauto]
].
Weakening holds for the bijection.
Lemma typ_bijection_weakening : forall E E' F F' G G' T1 T2,
typ_bijection (F ++ E) T1 (F' ++ E') T2 ->
senv_bijection E E' ->
senv_bijection F F' ->
senv_bijection G G' ->
ok (F ++ G ++ E) ->
ok (F' ++ G' ++ E') ->
typ_bijection (F ++ G ++ E) T1 (F' ++ G' ++ E') T2.
Lemma typ_bijection_weakening_head : forall E E' F F' T1 T2,
typ_bijection E T1 E' T2 ->
senv_bijection E E' ->
senv_bijection F F' ->
ok (F ++ E) ->
ok (F' ++ E') ->
typ_bijection (F ++ E) T1 (F' ++ E') T2.
Hint Resolve typ_bijection_weakening_head.
Substitution commutes with the bijection.
Lemma typ_bijection_subst : forall E E' F F' T1 T2 U1 U2 X,
typ_bijection (F ++ [(X,Typ)] ++ E) T1 (F' ++ [(X,Typ)] ++ E') T2 ->
typ_bijection E U1 E' U2 ->
typ_bijection (F ++ E) (A.subst_tt X U1 T1) (F' ++ E') (B.subst X U2 T2).
Lemma typ_bijection_rename : forall E E' T1 T2 (X Y : atom),
X <> Y ->
X `notin` (A.fv_tt T1 `union` B.fv T2) ->
ok ([(Y,Typ)] ++ E) ->
ok ([(Y,Typ)] ++ E') ->
senv_bijection E E' ->
typ_bijection ([(X,Typ)]++E) (A.open_tt T1 X) ([(X,Typ)]++E') (B.open T2 X) ->
typ_bijection ([(Y,Typ)]++E) (A.open_tt T1 Y) ([(Y,Typ)]++E') (B.open T2 Y).
Now prove that the bijection actually is a bijection.
Lemma typ_bijection_total : forall E T1,
A.wf_typ E T1 ->
exists E', exists T2, senv_bijection E E' /\ typ_bijection E T1 E' T2.
Lemma typ_bijection_unique : forall E E' T1 T2 T3,
typ_bijection E T1 E' T2 ->
typ_bijection E T1 E' T3 ->
T2 = T3.
Lemma typ_bijection_injective : forall E E' T1 T2 T3,
typ_bijection E T1 E' T3 ->
typ_bijection E T2 E' T3 -> T1 = T2.
Lemma typ_bijection_surjective : forall E' T2,
B.wf_typ E' T2 ->
exists E, exists T1, senv_bijection E E' /\ typ_bijection E T1 E' T2.
We first define a relation on well-formed Fsub expressions and
then show that the relation defines a bijection on such terms.
Inductive exp_bijection : A.senv -> A.exp ->
B.senv -> B.syntax -> Prop :=
| exp_bijection_var : forall E E' x,
ok E ->
ok E' ->
senv_bijection E E' ->
binds x Exp E ->
binds x Exp E' ->
exp_bijection E (A.exp_fvar x) E' (B.exp_fvar x)
| exp_bijection_abs : forall L E E' T T' e e',
typ_bijection E T E' T' ->
(forall x : atom, x `notin` L ->
exp_bijection ([(x,Exp)]++E) (A.open_ee e x) ([(x,Exp)]++E') (B.open e' x)) ->
exp_bijection E (A.exp_abs T e) E' (B.exp_abs T' (B.abs e'))
| exp_bijection_app : forall E E' e1 e1' e2 e2',
exp_bijection E e1 E' e1' ->
exp_bijection E e2 E' e2' ->
exp_bijection E (A.exp_app e1 e2) E' (B.exp_app e1' e2')
| exp_bijection_tabs : forall L E E' T T' e e',
typ_bijection E T E' T' ->
(forall X : atom, X `notin` L ->
exp_bijection ([(X,Typ)]++E) (A.open_te e X) ([(X,Typ)]++E') (B.open e' X)) ->
exp_bijection E (A.exp_tabs T e) E' (B.exp_tabs T' (B.abs e'))
| exp_bijection_tapp : forall E E' e e' T T',
exp_bijection E e E' e' ->
typ_bijection E T E' T' ->
exp_bijection E (A.exp_tapp e T) E' (B.exp_tapp e' T')
| exp_bijection_let : forall L E E' e1 e1' e2 e2',
exp_bijection E e1 E' e1' ->
(forall x : atom, x `notin` L ->
exp_bijection ([(x,Exp)]++E) (A.open_ee e2 x) ([(x,Exp)]++E') (B.open e2' x)) ->
exp_bijection E (A.exp_let e1 e2) E' (B.exp_let e1' (B.abs e2'))
| exp_bijection_inl : forall E E' e e',
exp_bijection E e E' e' ->
exp_bijection E (A.exp_inl e) E' (B.exp_inl e')
| exp_bijection_inr : forall E E' e e',
exp_bijection E e E' e' ->
exp_bijection E (A.exp_inr e) E' (B.exp_inr e')
| exp_bijection_case : forall L E E' e1 e1' e2 e2' e3 e3',
exp_bijection E e1 E' e1' ->
(forall x : atom, x `notin` L ->
exp_bijection ([(x,Exp)]++E) (A.open_ee e2 x) ([(x,Exp)]++E') (B.open e2' x)) ->
(forall x : atom, x `notin` L ->
exp_bijection ([(x,Exp)]++E) (A.open_ee e3 x) ([(x,Exp)]++E') (B.open e3' x)) ->
exp_bijection E (A.exp_case e1 e2 e3) E' (B.exp_case e1' (B.abs e2') (B.abs e3')).
Hint Constructors exp_bijection.
The relation contains only related environments.
Lemma exp_bijection_senv : forall E E' T1 T2,
exp_bijection E T1 E' T2 ->
senv_bijection E E'.
Hint Resolve exp_bijection_senv.
The relation contains only well-formed expressions.
Lemma exp_bijection_regular_1 : forall E E' e1 e2,
exp_bijection E e1 E' e2 ->
A.wf_exp E e1.
Hint Resolve exp_bijection_regular_1.
Lemma exp_bijection_regular_2 : forall E E' e1 e2,
exp_bijection E e1 E' e2 ->
B.wf_exp E' e2.
Hint Resolve exp_bijection_regular_2.
The bijection only holds for ok environments.
Lemma exp_bijection_ok_1 : forall E E' e1 e2,
exp_bijection E e1 E' e2 ->
ok E.
Hint Resolve exp_bijection_ok_1.
Lemma exp_bijection_ok_2 : forall E E' e1 e2,
exp_bijection E e1 E' e2 ->
ok E'.
Hint Resolve exp_bijection_ok_2.
Define a tactic to simplify proving well-formedness goals.
Ltac solve_lc_2 := try first [
solve [solve_lc_1]
| solve [apply expr_var]
| solve [eapply B''.expr_from_wf_exp; eapply exp_bijection_regular_2; eauto]
| solve [eapply A''.expr_from_wf_exp; eapply exp_bijection_regular_1; eauto]
].
Weakening holds for the bijection.
Lemma exp_bijection_weakening : forall E E' F F' G G' e1 e2,
exp_bijection (F ++ E) e1 (F' ++ E') e2 ->
senv_bijection E E' ->
senv_bijection F F' ->
senv_bijection G G' ->
ok (F ++ G ++ E) ->
ok (F' ++ G' ++ E') ->
exp_bijection (F ++ G ++ E) e1 (F' ++ G' ++ E') e2.
Lemma exp_bijection_weakening_head : forall E E' F F' e1 e2,
exp_bijection E e1 E' e2 ->
senv_bijection E E' ->
senv_bijection F F' ->
ok (F ++ E) ->
ok (F' ++ E') ->
exp_bijection (F ++ E) e1 (F' ++ E') e2.
Hint Resolve exp_bijection_weakening_head.
Substitution commutes with the bijection.
Lemma typ_bijection_strengthening : forall E E' F F' x T1 T2,
senv_bijection E E' ->
senv_bijection F F' ->
typ_bijection (F ++ [(x,Exp)] ++ E) T1 (F' ++ [(x,Exp)] ++ E') T2 ->
typ_bijection (F ++ E) T1 (F' ++ E') T2.
Lemma exp_bijection_subst_ee : forall E E' F F' e1 e2 U1 U2 X,
exp_bijection (F ++ [(X,Exp)] ++ E) e1 (F' ++ [(X,Exp)] ++ E') e2 ->
exp_bijection E U1 E' U2 ->
exp_bijection (F ++ E) (A.subst_ee X U1 e1) (F' ++ E') (B.subst X U2 e2).
Lemma exp_bijection_subst_te : forall E E' F F' e1 e2 U1 U2 X,
exp_bijection (F ++ [(X,Typ)] ++ E) e1 (F' ++ [(X,Typ)] ++ E') e2 ->
typ_bijection E U1 E' U2 ->
exp_bijection (F ++ E) (A.subst_te X U1 e1) (F' ++ E') (B.subst X U2 e2).
Lemma exp_bijection_rename_open_ee : forall E E' e1 e2 (x y : atom),
x <> y ->
x `notin` (A.fv_ee e1 `union` B.fv e2) ->
senv_bijection E E' ->
ok ([(y,Exp)]++E) ->
ok ([(y,Exp)]++E') ->
exp_bijection ([(x,Exp)]++E) (A.open_ee e1 x) ([(x,Exp)]++E') (B.open e2 x) ->
exp_bijection ([(y,Exp)]++E) (A.open_ee e1 y) ([(y,Exp)]++E') (B.open e2 y).
Lemma exp_bijection_rename_open_te : forall E E' e1 e2 (x y : atom),
x <> y ->
x `notin` (A.fv_te e1 `union` B.fv e2) ->
senv_bijection E E' ->
ok ([(y,Typ)]++E) ->
ok ([(y,Typ)]++E') ->
exp_bijection ([(x,Typ)]++E) (A.open_te e1 x) ([(x,Typ)]++E') (B.open e2 x) ->
exp_bijection ([(y,Typ)]++E) (A.open_te e1 y) ([(y,Typ)]++E') (B.open e2 y).
Now prove that the bijection actually is a bijection.
Lemma exp_bijection_total : forall E e,
A.wf_exp E e ->
exists E', exists e', senv_bijection E E' /\ exp_bijection E e E' e'.
Lemma exp_bijection_unique : forall E E' e1 e2 e3,
exp_bijection E e1 E' e2 ->
exp_bijection E e1 E' e3 ->
e2 = e3.
Lemma exp_bijection_injective : forall E E' e1 e2 e3,
exp_bijection E e1 E' e3 ->
exp_bijection E e2 E' e3 ->
e1 = e2.
Lemma exp_bijection_surjective : forall E' e2,
B.wf_exp E' e2 ->
exists E, exists e1, senv_bijection E E' /\ exp_bijection E e1 E' e2.
Inductive env_bijection : A.env -> B.env -> Prop :=
| env_bijection_nil :
env_bijection nil nil
| env_bijection_typ : forall E E' x T T',
x `notin` (dom E `union` dom E') ->
typ_bijection (A.to_senv E) T (B.to_senv E') T' ->
env_bijection E E' ->
env_bijection ([(x, A.bind_typ T)] ++ E) ([(x, B.bind_typ T')] ++ E')
| env_bijection_sub : forall E E' X T T',
X `notin` (dom E `union` dom E') ->
typ_bijection (A.to_senv E) T (B.to_senv E') T' ->
env_bijection E E' ->
env_bijection ([(X, A.bind_sub T)] ++ E) ([(X, B.bind_sub T')] ++ E').
Lemma env_bijection_senv : forall E E',
env_bijection E E' ->
senv_bijection (A.to_senv E) (B.to_senv E').
Hint Resolve env_bijection_senv.
Lemma env_bijection_binds_sub_1 : forall E E' X U,
binds X (A.bind_sub U) E ->
env_bijection E E' ->
exists U', typ_bijection (A.to_senv E) U (B.to_senv E') U' /\
binds X (B.bind_sub U') E'.
Lemma env_bijection_binds_sub_2 : forall E' E X U',
binds X (B.bind_sub U') E' ->
env_bijection E E' ->
exists U, typ_bijection (A.to_senv E) U (B.to_senv E') U' /\
binds X (A.bind_sub U) E.
Lemma env_bijection_binds_typ_1 : forall E E' X U,
binds X (A.bind_typ U) E ->
env_bijection E E' ->
exists U', typ_bijection (A.to_senv E) U (B.to_senv E') U' /\
binds X (B.bind_typ U') E'.
Lemma env_bijection_binds_typ_2 : forall E' E X U',
binds X (B.bind_typ U') E' ->
env_bijection E E' ->
exists U, typ_bijection (A.to_senv E) U (B.to_senv E') U' /\
binds X (A.bind_typ U) E.
The relation holds for only well-formed environments.
Lemma env_bijection_wf_env_1 : forall E E',
env_bijection E E' ->
A.wf_env E.
Hint Resolve env_bijection_wf_env_1.
Lemma env_bijection_wf_env_2 : forall E E',
env_bijection E E' ->
B.wf_env E'.
Hint Resolve env_bijection_wf_env_2.
These first lemmas are trivial, given our definitions.
Lemma wf_typ_bijection : forall E E' T T',
typ_bijection E T E' T' ->
(A.wf_typ E T <-> B.wf_typ E' T').
Lemma wf_exp_bijection : forall E E' e e',
exp_bijection E e E' e' ->
(A.wf_exp E e <-> B.wf_exp E' e').
Lemma wf_env_bijection : forall E E',
env_bijection E E' ->
(A.wf_env E <-> B.wf_env E').
We need a few corollaries of the substitution lemmas for
the bijections.
Lemma typ_bijection_subst_head : forall E E' T1 T2 U1 U2 X,
typ_bijection ([(X,Typ)] ++ E) T1 ([(X,Typ)] ++ E') T2 ->
typ_bijection E U1 E' U2 ->
typ_bijection E (A.subst_tt X U1 T1) E' (B.subst X U2 T2).
Lemma typ_bijection_subst_head_open : forall E E' T1 T2 U1 U2 X,
X `notin` (A.fv_tt T1 `union` B.fv T2) ->
typ_bijection ([(X,Typ)] ++ E) (A.open_tt T1 X) ([(X,Typ)] ++ E') (B.open T2 X) ->
typ_bijection E U1 E' U2 ->
typ_bijection E (A.open_tt T1 U1) E' (B.open T2 U2).
Lemma exp_bijection_subst_ee_head : forall E E' e1 e2 U1 U2 X,
exp_bijection ([(X,Exp)] ++ E) e1 ([(X,Exp)] ++ E') e2 ->
exp_bijection E U1 E' U2 ->
exp_bijection E (A.subst_ee X U1 e1) E' (B.subst X U2 e2).
Lemma exp_bijection_subst_ee_head_open : forall E E' e1 e2 U1 U2 X,
X `notin` (A.fv_ee e1 `union` B.fv e2) ->
exp_bijection ([(X,Exp)] ++ E) (A.open_ee e1 X) ([(X,Exp)] ++ E') (B.open e2 X) ->
exp_bijection E U1 E' U2 ->
exp_bijection E (A.open_ee e1 U1) E' (B.open e2 U2).
Lemma exp_bijection_subst_te_head : forall E E' e1 e2 U1 U2 X,
exp_bijection ([(X,Typ)] ++ E) e1 ([(X,Typ)] ++ E') e2 ->
typ_bijection E U1 E' U2 ->
exp_bijection E (A.subst_te X U1 e1) E' (B.subst X U2 e2).
Lemma exp_bijection_subst_te_head_open : forall E E' e1 e2 U1 U2 X,
X `notin` (A.fv_te e1 `union` B.fv e2) ->
exp_bijection ([(X,Typ)] ++ E) (A.open_te e1 X) ([(X,Typ)] ++ E') (B.open e2 X) ->
typ_bijection E U1 E' U2 ->
exp_bijection E (A.open_te e1 U1) E' (B.open e2 U2).
We now prove that the main Fsub relations derive the same sets of
judgements.
Lemma sub_bijection_1 : forall E E' T T' S S',
A.sub E S T ->
env_bijection E E' ->
typ_bijection (A.to_senv E) S (B.to_senv E') S' ->
typ_bijection (A.to_senv E) T (B.to_senv E') T' ->
B.sub E' S' T'.
Hint Resolve sub_bijection_1.
Lemma sub_bijection_2 : forall E E' T T' S S',
B.sub E' S' T' ->
env_bijection E E' ->
typ_bijection (A.to_senv E) S (B.to_senv E') S' ->
typ_bijection (A.to_senv E) T (B.to_senv E') T' ->
A.sub E S T.
Hint Resolve sub_bijection_2.
Lemma typing_bijection_1 : forall E E' e e' T T',
A.typing E e T ->
env_bijection E E' ->
exp_bijection (A.to_senv E) e (B.to_senv E') e' ->
typ_bijection (A.to_senv E) T (B.to_senv E') T' ->
B.typing E' e' T'.
Lemma typing_bijection_2 : forall E E' e e' T T',
B.typing E' e' T' ->
env_bijection E E' ->
exp_bijection (A.to_senv E) e (B.to_senv E') e' ->
typ_bijection (A.to_senv E) T (B.to_senv E') T' ->
A.typing E e T.
Lemma value_bijection_1 : forall E E' e e',
A.value e ->
exp_bijection E e E' e' ->
B.value e'.
Hint Resolve value_bijection_1.
Lemma value_bijection_2 : forall E E' e e',
B.value e' ->
exp_bijection E e E' e' ->
A.value e.
Hint Resolve value_bijection_2.
Lemma red_bijection_1 : forall E E' e1 e1' e2 e2',
A.red e1 e2 ->
exp_bijection E e1 E' e1' ->
exp_bijection E e2 E' e2' ->
B.red e1' e2'.
Lemma red_bijection_2 : forall E E' e1 e1' e2 e2',
B.red e1' e2' ->
exp_bijection E e1 E' e1' ->
exp_bijection E e2 E' e2' ->
A.red e1 e2.