cubical-transport-hott-lean4/CubicalTransport/Typing.lean
Maximus Gorog b9ca1d8875
Some checks are pending
Lean Action CI / build (push) Waiting to run
Modal cascade Phase 1: Syntax + Lean engine cascade
Per THEORY.md §3.1: cubical-native modal type formers as the engine
support layer for the cohesive modality triple (ʃ ⊣ ♭ ⊣ ♯).

CType (3 level-preserving formers):
  · CType.flat / .sharp / .shape : {ℓ} → CType ℓ → CType ℓ

CTerm (6 — three intros + three elims, modelled on .glueIn / .unglue):
  · CTerm.flatIntro / .sharpIntro / .shapeIntro  : CTerm → CTerm
  · CTerm.flatElim  / .sharpElim  / .shapeElim   : CTerm → CTerm → CTerm

Cascade: Syntax (constructors + SkeletalCType + skeleton + substDim);
DecEq (beq arms); Subst (substDim / substDimExpr + 6 rfl theorems);
DimLine (cascade through 8 dim-absent / dim-substitution lemma families);
Value (3 vIntro CVal + 3 nElim CNeu); Eval (β-reduction axioms +
stuck-neutral propagation, "marker neutral" idiom from vFst/vSnd
preserved); Readback (3 vIntro + 3 nElim arms with axioms); Typing
(6 HasType cases — bare recursion-principle shape; modal cohesion
dependent-motive form deferred to Phase 3); Reflect (3 reflectCType + 6
reflectCTerm + 3 reifyCType with level-coherence discharge + 6
reifyCTerm); Question (6 modal arms + 6 IsModalLine classifier
predicates with their Decidable instances); FFITest (cval/cterm
summary arms).

No Rust changes (Phase 2).  No Modal.lean module (Phase 3).  No
Crisp / CContext.crispVar / cohesive_triple theorems (Phase 3).

Build: lake build (48 jobs) + lake build CubicalTransport (42 jobs) PASS.
+664 lines across 11 files, 0 removed, 0 new sorries.

Honest deferrals documented:
  · Modal type-formers do not yet reduce under transport/comp; the
    match A blocks have wildcards so transp i (flat A) φ t produces a
    stuck ntransp neutral (correct under current axiom set; cohesion-
    driven reductions land in Phase 3).
  · HasType.flatElim et al carry the bare recursion-principle shape;
    the cohesive-HoTT-correct dependent-motive form requires the modal
    predicate lattice from Phase 3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 22:22:03 -06:00

364 lines
16 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/-
CubicalTransport.Typing
======================
The typing judgment Γ ⊢ t : A for the cubical term language,
universe-stratified (Layer 0 §0.1 cascade).
Rules:
var : membership in context
lam : introduce Π
app : eliminate Π
plam : introduce Path — Γ, i:I ⊢ t : A ⟹ Γ ⊢ ⟨i⟩t : Path A t[i:=0] t[i:=1]
papp : eliminate Path — t : Path A a b ⟹ t @ r : A
Face connection:
The face lattice tells us *when* a boundary holds (eq0 i / eq1 i).
`FaceFormula.eq0_iff_env_false` / `eq1_iff_env_true` (below) classify
which environments put us on the corresponding endpoint. The
boundary reduction itself is now an NbE theorem in `Readback.lean`
(`readback_papp_plam`); the previous step-level path β `PathWitness`
encoding has been removed alongside its underlying axiom.
## Universe-aware shape
Contexts pair each binder with a `Σ : ULevel, CType ` so individual
bindings can live at any universe level. `HasType` is universe-aware
via an implicit `{ : ULevel}` parameter on the type slot — each
judgement Γ ⊢ t : A carries the level of A as the implicit . Each
constructor's CType references take their own level (some constructors
bind two distinct levels for domain and codomain).
-/
import CubicalTransport.DimLine
-- ── Context ───────────────────────────────────────────────────────────────────
/-- A level-tagged CType bundle: pairs a CType with its universe level. -/
abbrev CTypeAny := Σ : ULevel, CType
/-- Typing context: ordered list of `(name, ⟨ℓ, A⟩)` bindings. Each
binder lives at its own universe level. -/
abbrev Ctx := List (String × CTypeAny)
-- ── Typing judgment ───────────────────────────────────────────────────────────
/-- The typing judgment Γ ⊢ t : A. Universe-aware: A's level lives at
the implicit `{ : ULevel}` slot. -/
inductive HasType : Ctx → CTerm → ∀ { : ULevel}, CType → Prop where
| var {Γ : Ctx} {x : String} { : ULevel} {A : CType } :
(x, ⟨ℓ, A⟩) ∈ Γ →
HasType Γ (.var x) A
| lam {Γ : Ctx} {x : String} { ' : ULevel}
{A : CType } {B : CType '} {t : CTerm} {var : String} :
HasType ((x, ⟨ℓ, A⟩) :: Γ) t B →
HasType Γ (.lam x t) (.pi var A B)
| app {Γ : Ctx} { ' : ULevel}
{A : CType } {B : CType '} {f a : CTerm} {var : String} :
HasType Γ f (.pi var A B) →
HasType Γ a A →
HasType Γ (.app f a) B
/-- Path introduction: ⟨i⟩t has type Path A t[i:=0] t[i:=1].
The boundaries are computed directly from substDim. -/
| plam {Γ : Ctx} { : ULevel} {A : CType } {t : CTerm} {i : DimVar} :
HasType Γ t A →
HasType Γ (.plam i t) (.path A (t.substDim i .zero) (t.substDim i .one))
/-- Path elimination: applying a path to any DimExpr gives the fibration type. -/
| papp {Γ : Ctx} { : ULevel} {A : CType } {t : CTerm}
{a b : CTerm} {r : DimExpr} :
HasType Γ t (.path A a b) →
HasType Γ (.papp t r) A
/-- Transport: if t has the type at the 0-end of line L,
then transpⁱ (λi.A) φ t has the type at the 1-end.
L packages the binder and body; we unpack to CTerm.transp's raw form. -/
| transp {Γ : Ctx} { : ULevel} {t : CTerm} {φ : FaceFormula} :
(L : DimLine ) →
HasType Γ t L.at0 →
HasType Γ (.transp L.binder L.body φ t) L.at1
/-- Composition: compⁱ (λi.A) φ u t has type L.at1, given:
· t : L.at0 (base term)
· u : L.at1 (system body typed at output end)
· on (φ ∩ i=0), u[i:=0] = t (compatibility side-condition)
The compat condition has the universally-quantified shape
∀ env, φ.eval env = true → env i = false → u[i:=0] = t
which is logically equivalent to
(∃ env, φ ∧ i=0) → u[i:=0] = t
— i.e. *if the face (φ ∩ i=0) is actually inhabited, the equation holds*.
This makes `.bot`-faced systems (unsatisfiable) vacuously compatible while
still forcing the equation wherever the face can actually be met.
The face formula φ and system body u are stored raw (no System wrapper)
to avoid a circular import; System.lean wraps these for ergonomics. -/
| comp {Γ : Ctx} { : ULevel} {t u : CTerm} {φ : FaceFormula} :
(L : DimLine ) →
HasType Γ t L.at0 →
HasType Γ u L.at1 →
(∀ env : DimVar → Bool,
φ.eval env = true → env L.binder = false →
CTerm.substDimBool L.binder false u = t) →
HasType Γ (.comp L.binder L.body φ u t) L.at1
/-- Σ introduction: pairing. Non-dependent form — `B` does not depend
on the first component's value. -/
| pair {Γ : Ctx} { ' : ULevel}
{A : CType } {B : CType '} {a b : CTerm} {var : String} :
HasType Γ a A →
HasType Γ b B →
HasType Γ (.pair a b) (.sigma var A B)
/-- Σ elimination (first projection). -/
| fst {Γ : Ctx} { ' : ULevel}
{A : CType } {B : CType '} {t : CTerm} {var : String} :
HasType Γ t (.sigma var A B) →
HasType Γ (.fst t) A
/-- Σ elimination (second projection). -/
| snd {Γ : Ctx} { ' : ULevel}
{A : CType } {B : CType '} {t : CTerm} {var : String} :
HasType Γ t (.sigma var A B) →
HasType Γ (.snd t) B
/-- Schema constructor application (REL1 minimal-viable typing).
Asserts that `.ctor S c params args` has type `.ind S params`.
No premises constrain `args` here — the per-argument typing
against `S.ctors[c].args` is enforced at runtime by `eval` and
the boundary system, not at the typing-judgement level. REL2
will refine this to a fully dependent rule with one premise per
`CtorSpec.args` entry.
Result level `` is user-specified at the type level (matching
`CType.ind`'s explicit level annotation). -/
| ctor {Γ : Ctx} { : ULevel} {S : CTypeSchema}
{params : List (Σ ' : ULevel, CType ')} {c : String} {args : List CTerm} :
HasType Γ (.ctor S c params args) (CType.ind ( := ) S params)
/-- Inductive eliminator (REL1 minimal-viable, *non-dependent* form).
The motive is treated as a function `.pi (.ind S params) C` for
some closed `C : CType` (constant motive), so the eliminator's
result type is `C`. Dependent motives — where `C` varies with
the eliminated value — are deferred to REL2.
Branch-level coherence (each branch body matches its ctor's
curried signature, including recursive-arg hypotheses for `.self`
args) is checked at runtime by `eval`, not statically here. -/
| indElim {Γ : Ctx} { ' : ULevel} {S : CTypeSchema}
{params : List (Σ '' : ULevel, CType '')}
{motive target : CTerm} {branches : List (String × CTerm)}
{C : CType '} {var : String} :
HasType Γ target (CType.ind ( := ) S params) →
HasType Γ motive (.pi var (CType.ind ( := ) S params) C) →
HasType Γ (.indElim S params motive branches target) C
/-- Dimension expression lifted to the term language.
Pre-REL2 (`Dev_REL1`) typed `.dimExpr r` at the placeholder
`.univ`. REL2 promotes the cubical interval to a first-class
CType (`CType.interval`) and types `.dimExpr r : .interval`.
Path-constructor dim arguments now carry real semantic ground:
`loop @ r`, `seg @ r`, `squash _ _ @ r`, etc. all type-check
against the corresponding `.dim` arg position. -/
| dimExpr {Γ : Ctx} {r : DimExpr} : HasType Γ (.dimExpr r) .interval
/-- Typing rule for `code`: `code A` has type `.univ ( := )` where
`A : CType `. The dual elimination rule is `CType.El`, whose
reduction `El (code A) = A` is the universe-code β-rule. -/
| code : ∀ {Γ : Ctx} { : ULevel} (A : CType ),
HasType Γ (.code A) (.univ ( := ))
/-- **Modal introduction (flat).** Given `a : A`, the term
`flatIntro a` inhabits `flat A`. Engine-layer rule —
modal-cohesion contextual restrictions (Crisp variables,
Π-modality interaction, etc.) land in Phase 3. -/
| flatIntro {Γ : Ctx} { : ULevel} {A : CType } {a : CTerm} :
HasType Γ a A →
HasType Γ (.flatIntro a) (.flat A)
/-- **Modal introduction (sharp).** -/
| sharpIntro {Γ : Ctx} { : ULevel} {A : CType } {a : CTerm} :
HasType Γ a A →
HasType Γ (.sharpIntro a) (.sharp A)
/-- **Modal introduction (shape).** -/
| shapeIntro {Γ : Ctx} { : ULevel} {A : CType } {a : CTerm} :
HasType Γ a A →
HasType Γ (.shapeIntro a) (.shape A)
/-- **Modal elimination (flat).** Given an eliminator `f : A → C`
and a scrutinee `m : flat A`, produce a term of type `C`.
Engine layer: this is the bare recursion-principle shape; the
modal-cohesion side-conditions (e.g. C must be flat-modal for
the elim to be well-formed in cohesive HoTT) are deferred to
Phase 3 (`Modal.lean`). At the engine layer the rule reflects
the recursion principle directly so that `eval` and `readback`
can dispatch on it. -/
| flatElim {Γ : Ctx} { ' : ULevel} {A : CType } {C : CType '}
{f m : CTerm} {var : String} :
HasType Γ f (.pi var A C) →
HasType Γ m (.flat A) →
HasType Γ (.flatElim f m) C
/-- **Modal elimination (sharp).** -/
| sharpElim {Γ : Ctx} { ' : ULevel} {A : CType } {C : CType '}
{f m : CTerm} {var : String} :
HasType Γ f (.pi var A C) →
HasType Γ m (.sharp A) →
HasType Γ (.sharpElim f m) C
/-- **Modal elimination (shape).** -/
| shapeElim {Γ : Ctx} { ' : ULevel} {A : CType } {C : CType '}
{f m : CTerm} {var : String} :
HasType Γ f (.pi var A C) →
HasType Γ m (.shape A) →
HasType Γ (.shapeElim f m) C
-- ── Structural rules ──────────────────────────────────────────────────────────
/-- Core: insert (x, B) into context Γ between a prefix Γ₁ and suffix Γ₂.
We take `Γ` as a free variable and carry `Γ = Γ₁ ++ Γ₂` as a hypothesis
so that `induction h` works (the index must be a variable). -/
private theorem HasType.weaken_core
(x : String) {B : ULevel} (B : CType B) (Γ₂ : Ctx)
{Γ : Ctx} {t : CTerm} { : ULevel} {A : CType }
(h : HasType Γ t A) :
∀ (Γ₁ : Ctx), Γ = Γ₁ ++ Γ₂ → HasType (Γ₁ ++ (x, ⟨B, B⟩) :: Γ₂) t A := by
induction h with
| var mem =>
intro Γ₁ hΓ; subst hΓ
apply HasType.var
rw [List.mem_append] at mem ⊢
rcases mem with h | h
· exact Or.inl h
· exact Or.inr (List.mem_cons.mpr (Or.inr h))
| lam ht ih =>
intro Γ₁ hΓ; subst hΓ
exact HasType.lam (ih ((_, _) :: Γ₁) rfl)
| app hf ha ihf iha =>
intro Γ₁ hΓ; subst hΓ
exact HasType.app (ihf Γ₁ rfl) (iha Γ₁ rfl)
| plam ht ih =>
intro Γ₁ hΓ; subst hΓ
exact HasType.plam (ih Γ₁ rfl)
| papp ht ih =>
intro Γ₁ hΓ; subst hΓ
exact HasType.papp (ih Γ₁ rfl)
| transp L ht ih =>
intro Γ₁ hΓ; subst hΓ
exact HasType.transp L (ih Γ₁ rfl)
| comp L ht hu hc iht ihu =>
intro Γ₁ hΓ; subst hΓ
exact HasType.comp L (iht Γ₁ rfl) (ihu Γ₁ rfl) hc
| pair ha hb iha ihb =>
intro Γ₁ hΓ; subst hΓ
exact HasType.pair (iha Γ₁ rfl) (ihb Γ₁ rfl)
| fst ht ih =>
intro Γ₁ hΓ; subst hΓ
exact HasType.fst (ih Γ₁ rfl)
| snd ht ih =>
intro Γ₁ hΓ; subst hΓ
exact HasType.snd (ih Γ₁ rfl)
| ctor =>
intro _ _; exact HasType.ctor
| indElim ht hm iht ihm =>
intro Γ₁ hΓ; subst hΓ
exact HasType.indElim (iht Γ₁ rfl) (ihm Γ₁ rfl)
| dimExpr =>
intro _ _; exact HasType.dimExpr
| code A =>
intro _ _; exact HasType.code A
| flatIntro ha ih =>
intro Γ₁ hΓ; subst hΓ
exact HasType.flatIntro (ih Γ₁ rfl)
| sharpIntro ha ih =>
intro Γ₁ hΓ; subst hΓ
exact HasType.sharpIntro (ih Γ₁ rfl)
| shapeIntro ha ih =>
intro Γ₁ hΓ; subst hΓ
exact HasType.shapeIntro (ih Γ₁ rfl)
| flatElim hf hm ihf ihm =>
intro Γ₁ hΓ; subst hΓ
exact HasType.flatElim (ihf Γ₁ rfl) (ihm Γ₁ rfl)
| sharpElim hf hm ihf ihm =>
intro Γ₁ hΓ; subst hΓ
exact HasType.sharpElim (ihf Γ₁ rfl) (ihm Γ₁ rfl)
| shapeElim hf hm ihf ihm =>
intro Γ₁ hΓ; subst hΓ
exact HasType.shapeElim (ihf Γ₁ rfl) (ihm Γ₁ rfl)
theorem HasType.weaken (x : String) {B : ULevel} (B : CType B)
{Γ : Ctx} {t : CTerm} { : ULevel} {A : CType }
(h : HasType Γ t A) : HasType ((x, ⟨B, B⟩) :: Γ) t A :=
HasType.weaken_core x B Γ h [] rfl
-- ── Face lattice connection ───────────────────────────────────────────────────
-- These lemmas tie the Bool-valued face semantics to the dimensional endpoints.
namespace FaceFormula
/-- The face (i=0) holds precisely when env i = false. -/
theorem eq0_iff_env_false (i : DimVar) (env : DimVar → Bool) :
(eq0 i).eval env = true ↔ env i = false := by
simp [eval]
/-- The face (i=1) holds precisely when env i = true. -/
theorem eq1_iff_env_true (i : DimVar) (env : DimVar → Bool) :
(eq1 i).eval env = true ↔ env i = true := by
simp [eval]
end FaceFormula
-- ── Typing inversion ─────────────────────────────────────────────────────────
/-- Inversion for plam: if ⟨i⟩t : Path A a b, then t : A and
the boundaries are exactly the substDim images. -/
theorem HasType.plam_inv
(Γ : Ctx) (i : DimVar) (t : CTerm) { : ULevel} (A : CType ) (a b : CTerm)
(h : HasType Γ (.plam i t) (.path A a b)) :
HasType Γ t A ∧
a = t.substDim i .zero ∧
b = t.substDim i .one := by
cases h with
| plam ht => exact ⟨ht, rfl, rfl⟩
/-- Inversion for papp: if t @ r : A, then t : Path A a b for some a b. -/
theorem HasType.papp_inv
(Γ : Ctx) (t : CTerm) (r : DimExpr) { : ULevel} (A : CType )
(h : HasType Γ (.papp t r) A) :
∃ a b, HasType Γ t (.path A a b) := by
cases h with
| papp ht => exact ⟨_, _, ht⟩
/-- Inversion for comp: reading off all components of a composition judgment.
We return an existential DimLine to avoid the naming clash between the
outer parameter and the constructor's internal binder. -/
theorem HasType.comp_inv
(Γ : Ctx) (i : DimVar) { : ULevel} (bodyA : CType ) (φ : FaceFormula)
(u t : CTerm) (A : CType )
(h : HasType Γ (.comp i bodyA φ u t) A) :
∃ L : DimLine , L.binder = i ∧ L.body = bodyA ∧
A = L.at1 ∧
HasType Γ t L.at0 ∧
HasType Γ u L.at1 ∧
(∀ env : DimVar → Bool,
φ.eval env = true → env L.binder = false →
CTerm.substDimBool L.binder false u = t) := by
cases h with
| comp L ht hu hc => exact ⟨L, rfl, rfl, rfl, ht, hu, hc⟩
/-- Inversion for transp: the output type is exactly L.at1. -/
theorem HasType.transp_inv
(Γ : Ctx) (i : DimVar) { : ULevel} (bodyA : CType ) (φ : FaceFormula)
(t : CTerm) (A : CType )
(h : HasType Γ (.transp i bodyA φ t) A) :
∃ L : DimLine , L.binder = i ∧ L.body = bodyA ∧
A = L.at1 ∧ HasType Γ t L.at0 := by
cases h with
| transp L ht => exact ⟨L, rfl, rfl, rfl, ht⟩