Source code on Github
{-# OPTIONS --allow-unsolved-metas #-}
-- {-# OPTIONS --auto-inline #-}
open import Prelude.Init hiding ([_]); open SetAsType
open L.Mem
open import Prelude.General
open import Prelude.DecEq
-- open import Prelude.Lists.Dec
-- open import Prelude.Measurable
open import Prelude.InfEnumerable
open import Prelude.Setoid
open import Prelude.InferenceRules

-- ** Substitution.
module ULC.Substitution (Atom : Type)  _ : DecEq Atom   _ : Enumerable∞ Atom  where

open import ULC.Base    Atom  it 
open import ULC.Measure Atom  it   it 
open import ULC.Alpha   Atom  it   it 
open import Nominal Atom
open import Nominal.Product Atom

-- enforce the Barendregt convention: no shadowing, distinct bound variables
{-# TERMINATING #-}
barendregt : Op₁ Term
barendregt = go []
  where
    go : List Atom  Op₁ Term
    go xs = λ where
      (` x)      ` x
      (l · r)    go xs l · go xs r
      (ƛ x  t)  let x′ = freshAtom (xs ++ supp t)
                  in ƛ x′  go (x  xs) (swap x′ x t)

infix 6 _[_/_]
{-# TERMINATING #-}
_[_/_] : Term  Atom  Term  Term
(` x) [ 𝕒 / N ] = if x == 𝕒 then N else ` x
(L · M) [ 𝕒 / N ] =
  let L′ = L [ 𝕒 / N ]
      M′ = M [ 𝕒 / N ]
  in L′ · M′
(ƛ ) [ 𝕒 / N ] =
  -- let y = fresh-var (𝕒 , t̂ , N)
  let y = freshAtom (𝕒  supp  ++ supp N)
  in ƛ y  conc  y [ 𝕒 / N ]

infix 6 _[_]
_[_] : Abs Term  Term  Term
(abs x t) [ s ] = (ƛ x  t) [ x / s ]

infix 6 _[_/_]↑
_[_/_]↑ : Abs Term  Atom  Term  Abs Term
(abs 𝕒 t) [ x / N ]↑ = unƛ $ (ƛ 𝕒  t) [ x / N ]

{- ** well-founded version
t₀ [ 𝕒 / s ] = ≺-rec _ go t₀
  module ∣Substitution∣ where
    go : ∀ x → (∀ y → y ≺ x → Term) → Term
    go x rec with x
    ... | ` x   = if x == 𝕒 then s else ` x
    ... | l · m = rec l (l ·≺ˡ m) · rec m (l ·≺ʳ m)
    -- Cannot simply use `ƛ (mapAbs go f)` here; need well-founded recursion
    -- ... | ƛ f   = ƛ mapAbs-Term f (λ t t≺ → rec t t≺)
    ... | ƛ f =
      let y , _ = fresh (nub $ 𝕒 ∷ supp f ++ supp s)
      in  ƛ y ⇒ rec (conc f y) (conc≺ f y)
-}

-- ** postulate equivariance of substitution for now...
postulate swap-subst : Equivariant _[_/_]
-- swap-subst = ? -- equivariant _[_/_]

-- ** we will also need the following lemmas for proving Reduction.sub-par (β case)

subst-commute : N [ x / L ] [ y / M [ x / L ] ]  N [ y / M ] [ x / L ]
subst-commute {` n} {x} {L} {y} {M}
  with n  x | n  y
... | yes refl | yes refl
  -- exclude with x ≠ y
  = {!subst-commute !}
... | yes refl | no n≠y
  rewrite ≟-refl n
  = {!!}
  -- prove with y ♯ L
... | no n≠x | yes refl
  rewrite ≟-refl n
  = ≈-refl
... | no n≠x | no n≠y
  rewrite dec-no (n  x) n≠x .proj₂
        | dec-no (n  y) n≠y .proj₂
        = ≈-refl
subst-commute { · } {x} {L} {y} {M}
  = ξ≡ (subst-commute {}) (subst-commute {})
subst-commute {ƛ } {x} {L} {y} {M}
  with freshAtom (x  supp  ++ supp L)
  -- (ƛ xˡ ⇒ conc t̂ xˡ [ x / L ]) [ y / M [ x / L ] ]
  with freshAtom (y  supp (abs  $ conc   [ x / L ]) ++ supp (M [ x / L ]))
  -- ƛ yˡ ⇒ conc (abs xˡ $ conc t̂ xˡ [ x / L ]) yˡ [ y / M [ x / L ] ]
  --      ≡ conc t̂ yˡ [ x / L ] [ y / M [ x / L ] ]

  with freshAtom (y  supp  ++ supp M)
  -- (ƛ yʳ ⇒ conc t̂ yʳ [ y / M ]) [ x / L ]
  with freshAtom (x  supp (abs  $ conc   [ y / M ]) ++ supp L)
  -- ƛ xʳ ⇒ conc (abs yʳ $ conc t̂ yʳ [ y / M ]) xʳ [ x / L ]
  --      ≡ conc t̂ xʳ [ y / M ] [ x / L ]
  = ζ≡ ({!!} ,  z z∉  {!!}))

postulate cong-subst : t  t′  t [ x / M ]  t′ [ x / M ]

-- {-# TERMINATING #-}
swap∘subst : swap y x N [ y / M ]  N [ x / M ]
swap∘subst {y} {x} {` n} {M}
  with n  x | n  y
... | yes refl | yes refl
  rewrite ≟-refl y
  = ≈-refl
... | yes refl | no n≠y
  rewrite ≟-refl y
  = ≈-refl
... | no n≠x | yes refl
  rewrite dec-no (x  y) (≢-sym n≠x) .proj₂
  = {!!} -- prove with y ♯ N
... | no n≠x | no n≠y
  rewrite dec-no (n  y) n≠y .proj₂
  = ≈-refl
swap∘subst {y} {x} {L · R} {M}
  = ξ≡ (swap∘subst {N = L}) (swap∘subst {N = R})
swap∘subst {y} {x} {ƛ } {M}
{-
swap y x (ƛ z ⇒ t) [ y / M ]
≡ (ƛ swap y x z ⇒ swap y x t) [ y / M ]
≡ let zˡ = freshAtom (y ∷ supp (swap y z (ƛ z ⇒ t) ++ supp M)
  in ƛ zˡ → conc (swap y x $ abs z t) zˡ [ y / M ]
          ≡ conc (swap y x $ abs z t) zˡ [ swap y x x / M ]
          ≡ conc (swap y x $ abs z t) (swap y x zˡ) [ swap y x x / M ]

≡ let zʳ = freshAtom (x ∷ z ∷ supp t ++ supp M)
  in ƛ zʳ → conc (ƛ z ⇒ t) zʳ [ x / M ]
≡ (ƛ z ⇒ t) [ x / M ]
∎

conc (ƛ zˡ → conc (ƛ swap y x z ⇒ swap y x t) zˡ [ y / M ]) w
≡ swap w zˡ $ conc (ƛ swap y x z ⇒ swap y x t) zˡ [ y / M ]
≡ swap w zˡ $ conc (ƛ swap y x z ⇒ swap y x t) zˡ [ swap y x x / M ]

≡ conc (swap w zˡ $ ƛ swap y x z ⇒ swap y x t) w [ y / M ]
≡ conc (ƛ swap w zˡ (swap y x z) ⇒ swap w zˡ $ swap y x t) w [ y / M ]
≡ conc (ƛ swap w zˡ (swap y x z) ⇒ swap w zˡ $ swap y x t) w [ y / M ]

≈?
≡ swap w z t [ x / M ]
≡ conc (ƛ z ⇒ t) w [ x / M ]
≡ conc (swap w zʳ $ ƛ z ⇒ t) w [ x / M ]
≡ swap w zʳ $ conc (ƛ z ⇒ t) zʳ [ x / M ]
conc (ƛ zʳ → conc (ƛ z ⇒ t) zʳ [ x / M ]) w
-}
  = ζ≡ ({!!} , λ w w∉  {!!})