-- Typed βη equality for the simply typed lambda calculus.
--
-- β would be the same for the untyped lambda calculus,
-- but for η it is convenient that we have the type information.

module Term.Equality where

open import Prelude
open import Term
open import Term.Weakening
open import Term.Substitution

open import Term.Weakening.Properties using (wk1-wk)
open import Term.Substitution.Properties using (wk-sg; sub-sg; wk1-sub)

private
  variable
    Γ Δ : Context
    a b : Ty
    t t' t'' t₁ t₂ t₃ u u' : Term Γ a
    σ σ' : Sub Γ Δ
    ρ ρ' : Wk Γ Δ

infix 1 _≅_

-- t ≅ t' is the congruence generated by axioms from β and η,
-- congruence meaning that it is an equivalence relation (refl, trans, sym)
-- and closed under all term constructors (abs, app).
--
-- Closure under weakening and substitution is proven subsequently.

data _≅_ : (t t' : Term Γ a)  Set where
  β      : app (abs t) u  t [ u ]₀
  η      : {t : Term Γ (a  b)}  t  abs (app (wk1 t) (var zero))
  abs    : (et : t  t')  abs t  abs t'
  app    : (et : t  t') (eu : u  u')  app t u  app t' u'
  refl   : t  t
  ≅trans : (e₁ : t₁  t₂) (e₂ : t₂  t₃)  t₁  t₃
  ≅sym   : (e : t  t')  t'  t

-- Closure of t ≅ t' under weakening.
--
-- Proof by induction on t ≅ t'.
-- Only the base cases (β and η) are interesting, they need properties
-- about weakening and substitution.

wk-≅ : t  t'  wk ρ t  wk ρ t'

-- Case β-contraction: Show wk ρ (app (abs t) u) ≅  wk ρ (t [ u ]₀).
-- By definition of wk:     wk ρ (app (abs t) u) = app (abs (wk (keep ρ) t)) (wk ρ u)
-- Thus, β gives:           wk ρ (app (abs t) u) ≅ (wk (keep ρ) t) [ wk ρ u ]₀
-- We need the fact that wk over the single substitution,
--    wk-sg : (wk (keep ρ) t) [ wk ρ u ]₀ ≡ wk ρ (t [ u ]₀)
-- to conclude the proof by a cast ("subst").
wk-≅ {ρ = ρ} (β {t = t} {u = u}) =
  subst
    (app (abs (wk (keep ρ) t)) (wk ρ u) ≅_)
    (wk-sg {t = t})  -- wk (keep ρ) t) [ wk ρ u ]₀  ≡ wk ρ (t [ u ]₀)
    β

-- Case η-expansion: Show wk ρ t ≅ wk ρ (abs (app (wk1 t) (var zero)))
--                               = abs (wk (keep ρ) (app (wk1 t) (var zero)))
--                               = abs (app (wk (keep ρ) (wk1 t)) (wk (keep ρ) (var zero)))
--                               = abs (app (wk (keep ρ) (wk1 t)) (var (lookup (keep ρ) zero)))
--                               = abs (app (wk (keep ρ) (wk1 t)) (var zero))
-- η gives  wk ρ t ≅ abs (app (wk1 (wk ρ t)) (var zero)
-- So we need to cast with  wk1-wk : wk1 (wk ρ t) ≡ wk (keep ρ) (wk1 t).
wk-≅ {ρ = ρ} (η {t = t}) =
  subst
     u  wk ρ t  abs (app u (var zero)))
    wk1-wk
    (η {t = wk ρ t})

-- Other cases by induction hypothesis:
wk-≅ (abs e)             = abs (wk-≅ e)
wk-≅ (app e e₁)          = app (wk-≅ e) (wk-≅ e₁)
wk-≅ refl                = refl
wk-≅ (≅trans e e₁)       = ≅trans (wk-≅ e) (wk-≅ e₁)
wk-≅ (≅sym e)            = ≅sym (wk-≅ e)

-- Closure of t ≅ t' under substitution.
--
-- Proof by induction on t ≅ t'.
-- Only the base cases (β and η) are interesting, they need the substitution laws.

sub-≡ : t  t'  sub σ t  sub σ t'

-- Case β-contraction: sub σ (app (abs t) u) ≅ sub σ (t [ u ]₀)
sub-≡ {σ = σ} (β {t = t} {u = u}) =
  subst
    (app (abs (sub (lift σ) t)) (sub σ u) ≅_)
    (sym (sub-sg {σ = σ} {t = t} {u = u}))
    β

-- Case η-expansion: sub σ t ≅ sub σ (abs (app (wk1 t) (var zero)))
sub-≡ {σ = σ} (η {t = t}) =
  subst
     u  sub σ t  abs (app u (var zero)))
    (wk1-sub {σ = σ} {t = t})
    (η {t = sub σ t})

-- Other cases by induction hypothesis:
sub-≡ {σ = σ} (abs e)       = abs (sub-≡ e)
sub-≡ {σ = σ} (app e e₁)    = app (sub-≡ e) (sub-≡ e₁)
sub-≡ {σ = σ} refl          = refl
sub-≡ {σ = σ} (≅trans e e₁) = ≅trans (sub-≡ e) (sub-≡ e₁)
sub-≡ {σ = σ} (≅sym e)      = ≅sym (sub-≡ e)