|
| 1 | +/- |
| 2 | +Copyright (c) 2026 Elimia (Sehun Kim). All rights reserved. |
| 3 | +Released under Apache 2.0 license as described in the file LICENSE. |
| 4 | +Authors: Elimia (Sehun Kim) |
| 5 | +-/ |
| 6 | + |
| 7 | +module |
| 8 | + |
| 9 | +public import Cslib.Languages.LambdaCalculus.LocallyNameless.Untyped.Properties |
| 10 | + |
| 11 | +@[expose] public section |
| 12 | + |
| 13 | +/-! |
| 14 | +
|
| 15 | +Alternative Definitions for LC: |
| 16 | +
|
| 17 | +This module defines `LcAt k M`, a more general definition of local closure. When k = 0, this is |
| 18 | +equivalent to `LC`, as shown in `lcAt_iff_LC`. |
| 19 | +
|
| 20 | +-/ |
| 21 | + |
| 22 | +namespace Cslib.LambdaCalculus.LocallyNameless.Untyped.Term |
| 23 | + |
| 24 | +universe u |
| 25 | + |
| 26 | +variable {Var : Type u} |
| 27 | + |
| 28 | +/-- `LcAt k M` is satisfied when all bound indices of M are smaller than `k`. -/ |
| 29 | +@[simp, scoped grind =] |
| 30 | +def LcAt (k : ℕ) : Term Var → Prop |
| 31 | +| bvar i => i < k |
| 32 | +| fvar _ => True |
| 33 | +| app t₁ t₂ => LcAt k t₁ ∧ LcAt k t₂ |
| 34 | +| abs t => LcAt (k + 1) t |
| 35 | + |
| 36 | +/-- `depth` counts the maximum number of the lambdas that are enclosing variables. -/ |
| 37 | +@[simp, scoped grind =] |
| 38 | +def depth : Term Var → ℕ |
| 39 | +| bvar _ => 0 |
| 40 | +| fvar _ => 0 |
| 41 | +| app t₁ t₂ => max (depth t₁) (depth t₂) |
| 42 | +| abs t => depth t + 1 |
| 43 | + |
| 44 | +@[elab_as_elim] |
| 45 | +protected lemma ind_on_depth (P : Term Var → Prop) (bvar : ∀ i, P (bvar i)) (fvar : ∀ x, P (fvar x)) |
| 46 | + (app : ∀ M N, P M → P N → P (app M N)) (abs : ∀ M, P M → (∀ N, N.depth ≤ M.depth → P N) → P M.abs) |
| 47 | + (M : Term Var) : P M := by |
| 48 | + have h {d : ℕ} {M : Term Var} (p : M.depth ≤ d) : P M := by |
| 49 | + induction d generalizing M with |
| 50 | + | zero => induction M <;> grind |
| 51 | + | succ => |
| 52 | + induction M with |
| 53 | + | abs M' => apply abs M' <;> grind |
| 54 | + | _ => grind [sup_le_iff] |
| 55 | + exact h M.depth.le_refl |
| 56 | + |
| 57 | +/-- The depth of the lambda expression doesn't change by opening at i-th bound variable |
| 58 | + for some free variable. -/ |
| 59 | + @[simp, scoped grind =] |
| 60 | +lemma depth_openRec_fvar_eq_depth (M : Term Var) (x : Var) (i : ℕ) : |
| 61 | + (M⟦i ↝ fvar x⟧).depth = M.depth := by |
| 62 | + induction M generalizing i <;> grind |
| 63 | + |
| 64 | +/-- The depth of the lambda expression doesn't change by opening for some free variable. -/ |
| 65 | +theorem depth_open_fvar_eq_depth (M : Term Var) (x : Var) : depth (M ^ fvar x) = depth M := |
| 66 | + depth_openRec_fvar_eq_depth M x 0 |
| 67 | + |
| 68 | +/-- Opening for some free variable at i-th bound variable, increments `LcAt`. -/ |
| 69 | +@[simp, scoped grind =] |
| 70 | +theorem lcAt_openRec_fvar_iff_lcAt (M : Term Var) (x : Var) (i : ℕ) : |
| 71 | + LcAt i (M⟦i ↝ fvar x⟧) ↔ LcAt (i + 1) M := by |
| 72 | + induction M generalizing i <;> grind |
| 73 | + |
| 74 | +/-- Opening for some free variable is locally closed if and only if `M` is `LcAt 1`. -/ |
| 75 | +theorem lcAt_open_fvar_iff_lcAt (M : Term Var) (x : Var) : LcAt 0 (M ^ fvar x) ↔ LcAt 1 M := |
| 76 | + lcAt_openRec_fvar_iff_lcAt M x 0 |
| 77 | + |
| 78 | +/-- `M` is `LcAt 0` if and only if `M` is locally closed. -/ |
| 79 | +theorem lcAt_iff_LC (M : Term Var) [HasFresh Var] : LcAt 0 M ↔ M.LC := by |
| 80 | + induction M using LambdaCalculus.LocallyNameless.Untyped.Term.ind_on_depth with |
| 81 | + | abs => |
| 82 | + constructor |
| 83 | + · grind [LC.abs ∅] |
| 84 | + · intros h2 |
| 85 | + rcases h2 with ⟨⟩|⟨L,_,_⟩ |
| 86 | + grind [fresh_exists L] |
| 87 | + | _ => grind [cases LC] |
| 88 | + |
| 89 | +end Cslib.LambdaCalculus.LocallyNameless.Untyped.Term |
0 commit comments