Require Import Omega. Require Import Autosubst.Autosubst. Require Import AutosubstExtra. (* just for [upn_ren] *) Require Import MyTactics. (* TEMPORARY *) (* This file defines the construction [eos x t], which can be understood as an end-of-scope mark for [x] in the term [t]. *) (* It also defines the single-variable substitution t.[u // x], which is the substitution of [u] for [x] in [t]. *) (* -------------------------------------------------------------------------- *) Section EOS. Context {A} `{Ids A, Rename A, Subst A, SubstLemmas A}. (* The substitution [Var 0 .: Var 1 .: ... .: Var (x-1) .: Var (x+1) .: ...] does not have [Var x] in its codomain. Thus, applying this substitution to a term [t] can be understood as an end-of-scope construct: it means ``end the scope of [x] in [t]''. We write [eos x t] for this construct. It is also known as [adbmal]: see Hendriks and van Oostrom, https://doi.org/10.1007/978-3-540-45085-6_11 *) (* There are at least two ways of defining the above substitution. One way is to define it in terms of AutoSubst combinators: *) Definition eos_var x : var -> var := (iterate upren x (+1)). Definition eos x t := t.[ren (eos_var x)]. Lemma eos_eq: forall x t, t.[upn x (ren (+1))] = eos x t. Proof. intros. unfold eos, eos_var. erewrite upn_ren by tc. reflexivity. Qed. (* Another way is to define directly as a function of type [var -> var]. *) Definition lift_var x : var -> var := fun y => if le_gt_dec x y then 1 + y else y. (* The two definitions coincide: *) Lemma upren_lift_var: forall x, upren (lift_var x) = lift_var (S x). Proof. intros. f_ext; intros [|y]. { reflexivity. } { simpl. unfold lift_var, var. dblib_by_cases; omega. } Qed. Lemma eos_var_eq_lift_var: eos_var = lift_var. Proof. (* An uninteresting proof. *) f_ext; intros x. unfold eos_var. induction x. { reflexivity. } { rewrite iterate_S. rewrite IHx. rewrite upren_lift_var. reflexivity. } Qed. (* -------------------------------------------------------------------------- *) (* [eos] enjoys certain commutation laws. *) (* Ending the scope of variable [k], then the scope of variable [s], is the same as first ending the scope of variable [1 + s], then ending the scope of variable [k]. This holds provided [k <= s] is true, i.e., [k] is the most recently-introduced variable.*) Lemma lift_var_lift_var: forall k s, k <= s -> lift_var s >>> lift_var k = lift_var k >>> lift_var (S s). Proof. (* By case analysis. *) intros. f_ext; intros x. asimpl. unfold lift_var, var. dblib_by_cases; omega. Qed. Lemma eos_eos: forall k s t, k <= s -> eos k (eos s t) = eos (1 + s) (eos k t). Proof. intros. unfold eos. asimpl. rewrite eos_var_eq_lift_var. rewrite lift_var_lift_var by eauto. reflexivity. Qed. (* What about the case where [k] is the least recently-introduced variable? It is obtained by symmetry, of course. *) Lemma eos_eos_reversed: forall k s t, k >= s + 1 -> eos k (eos s t) = eos s (eos (k - 1) t). Proof. intros. replace k with (1 + (k - 1)) by omega. rewrite <- eos_eos by omega. replace (1 + (k - 1) - 1) with (k - 1) by omega. reflexivity. Qed. (* -------------------------------------------------------------------------- *) (* Single-variable substitutions. *) (* [subst_var u x] is the substitution of [u] for [x]. *) (* We give a direct definition of it as a function of type [var -> term], defined by cases. I don't know if it could also be nicely defined in terms of the basic combinators of de Bruijn algebra. Note that the candidate definition [upn x (t .: ids)] is WRONG when [x > 0]. *) Definition subst_var (u : A) (x y : var) : A := match lt_eq_lt_dec y x with | inleft (left _) => ids y | inleft (right _) => u | inright _ => ids (y - 1) end. (* A nice notation: [t.[u // x]] is the substitution of [u] for [x] in [t]. *) Notation "t .[ u // x ]" := (subst (subst_var u x) t) (at level 2, u at level 200, left associativity, format "t .[ u // x ]") : subst_scope. (* The following laws serve as sanity checks: we got the definition right. *) Lemma subst_var_miss_1: forall x y u, y < x -> (ids y).[u // x] = ids y. Proof. intros. asimpl. unfold subst_var. dblib_by_cases. reflexivity. Qed. Lemma subst_var_match: forall x u, (ids x).[ u // x ] = u. Proof. intros. asimpl. unfold subst_var. dblib_by_cases. reflexivity. Qed. Lemma subst_var_miss_2: forall x y u, x < y -> (ids y).[u // x] = ids (y - 1). Proof. intros. asimpl. unfold subst_var. dblib_by_cases. reflexivity. Qed. (* In the special case where [x] is 0, the substitution [t // 0] can also be written [t/], which is an AutoSubst notation for [t .: ids]. *) Lemma subst_var_0: forall t u, t.[u // 0] = t.[u/]. Proof. intros. f_equal. clear t. f_ext. intros [|x]. { reflexivity. } { unfold subst_var. simpl. f_equal. omega. } Qed. (* -------------------------------------------------------------------------- *) (* A cancellation law: substituting for a variable [x] that does not occur in [t] yields just [t]. In other words, a substitution for [x] vanishes when it reaches [eos x _]. *) (* In informal syntax, this lemma would be written: t[u/x] = t under the hypothesis that x does not occur free in t. In de Bruijn style, the statement is just as short, and does not have a side condition. Instead, it requires an explicit [eos x _] to appear at the root of the term to which the substitution is applied; this may require rewriting before this lemma can be applied. *) Lemma subst_eos: forall x t u, (eos x t).[u // x] = t. Proof. intros. (* Again, let's simplify this first. *) unfold eos. asimpl. (* Aha! We can forget about [t], and focus on proving that two substitutions are equal. To do so, it is sufficient that their actions on a variable [y] are the same. *) rewrite <- subst_id. f_equal. clear t. f_ext. intro y. (* The proof is easy if we replace [eos_var] with [lift_var]. *) rewrite eos_var_eq_lift_var. simpl. unfold subst_var, lift_var. dblib_by_cases; f_equal; omega. Qed. (* The above property allows us to prove that [eos x _] is injective. Indeed, it has an inverse, namely [u // x], where [u] is arbitrary. *) Lemma eos_injective: forall x t1 t2, eos x t1 = eos x t2 -> t1 = t2. Proof. intros. pose (u := t1). (* dummy *) erewrite <- (subst_eos x t1 u). erewrite <- (subst_eos x t2 u). congruence. Qed. (* -------------------------------------------------------------------------- *) (* More commutation laws. *) Lemma eos_subst_1: forall k s t u, k <= s -> eos k (t.[u // s]) = (eos k t).[eos k u // s + 1]. Proof. intros. unfold eos. asimpl. f_equal. clear t. rewrite eos_var_eq_lift_var. f_ext. intros y. asimpl. unfold subst_var, lift_var. dblib_by_cases; asimpl; dblib_by_cases; solve [ eauto | f_equal; omega ]. Qed. Lemma eos_subst_2: forall k s t u, s <= k -> eos k (t.[u // s]) = (eos (k + 1) t).[eos k u // s]. Proof. intros. unfold eos. asimpl. f_equal. clear t. rewrite eos_var_eq_lift_var. f_ext. intros y. asimpl. unfold subst_var, lift_var. dblib_by_cases; asimpl; dblib_by_cases; solve [ eauto | f_equal; omega ]. Qed. Lemma subst_subst: forall t k v s w, k <= s -> t.[w // k].[v // s] = t.[eos k v // 1 + s].[w.[v // s] // k]. Proof. (* First, get rid of [t]. It is sufficient to consider the action of these substitutions at a variable [y]. *) intros. asimpl. f_equal. clear t. f_ext. intros y. (* Replace [eos_var] with [lift_var], whose definition is more direct. *) unfold eos. rewrite eos_var_eq_lift_var. (* Then, use brute force (case analysis) to prove that the goal holds. *) unfold subst_var. simpl. dblib_by_cases; asimpl; dblib_by_cases; (* This case analysis yields 5 cases, of which 4 are trivial... *) eauto. (* ... thus, one case remains. *) (* Now get rid of [v]. It is again sufficient to consider the action of these substitutions at a variable [z]. *) replace v with v.[ids] at 1 by autosubst. f_equal. f_ext. intros z. simpl. (* Again, use brute force. *) unfold lift_var. dblib_by_cases; f_equal. unfold var. omega. (* Not really proud of this proof. *) Qed. Lemma pun_1: forall t x, (eos x t).[ ids x // x + 1 ] = t. Proof. (* First, get rid of [t]. It is sufficient to consider the action of these substitutions at a variable [y]. *) intros. unfold eos. asimpl. replace t with t.[ids] at 2 by autosubst. f_equal. clear t. f_ext. intros y. (* Replace [eos_var] with [lift_var], whose definition is more direct. *) rewrite eos_var_eq_lift_var. (* Then, use brute force (case analysis) to prove that the goal holds. *) simpl. unfold subst_var, lift_var. dblib_by_cases; f_equal; unfold var; omega. Qed. Lemma pun_2: forall t x, (eos (x + 1) t).[ ids x // x ] = t. Proof. (* First, get rid of [t]. It is sufficient to consider the action of these substitutions at a variable [y]. *) intros. unfold eos. asimpl. replace t with t.[ids] at 2 by autosubst. f_equal. clear t. f_ext. intros y. (* Replace [eos_var] with [lift_var], whose definition is more direct. *) rewrite eos_var_eq_lift_var. (* Then, use brute force (case analysis) to prove that the goal holds. *) simpl. unfold subst_var, lift_var. dblib_by_cases; f_equal; unfold var; omega. Qed. End EOS. (* Any notations defined in the above section must now be repeated. *) Notation "t .[ u // x ]" := (subst (subst_var u x) t) (at level 2, u at level 200, left associativity, format "t .[ u // x ]") : subst_scope. (* The tactic [subst_var] attempts to simplify applications of [subst_var]. *) Ltac subst_var := first [ rewrite subst_var_miss_1 by omega | rewrite subst_var_match by omega | rewrite subst_var_miss_2 by omega ].