cubical-transport-hott-lean4/CubicalTransport/DecEq.lean
Maximus Gorog 6e4936d6ee
Some checks are pending
Lean Action CI / build (push) Waiting to run
Refactor Phase 2: modal unification — Lean engine cascade
Per the elegance pass: 9 ad-hoc per-modality constructors collapse into
3 ModalityKind-parameterised constructors.  Future modalities (Phase
4's ʃ_EML, ℑ infinitesimal) extend ModalityKind by adding cases —
no new constructors, no new ABI bump.

New Lean enum (Syntax.lean):
  inductive ModalityKind | flat | sharp | shape
    deriving DecidableEq, Repr, Inhabited

Constructor unification:
  · CType: 3 (.flat / .sharp / .shape) → 1 (.modal k A)
  · CTerm: 6 (.flatIntro / .sharpIntro / .shapeIntro / .flatElim /
            .sharpElim / .shapeElim) → 2 (.modalIntro k a, .modalElim k f m)
  · CVal:  3 (vFlatIntro / vSharpIntro / vShapeIntro) → 1 (vModalIntro)
  · CNeu:  3 (nflatElim / nsharpElim / nshapeElim) → 1 (nModalElim)
  · SkeletalCType: 3 (skFlat / skSharp / skShape) → 1 (skModal k)

Engine cascade across 12 files (DecEq, DimLine, Eval, FFITest, Modal,
Question, Readback, Reflect, Subst, Syntax, Typing, Value): every
match site collapsed from 3-per-modality arms to 1 k-parameterised arm.

Reflect.lean: new `reflectModalityKind` / `reifyModalityKind` helpers
+ ModalityKind dispatch arm in classifyFieldType.  The Phase 1 macro
auto-derived per-constructor reflect/reify for the new unified
constructors — no manual cascade needed there.

Eval.lean β-rule: `.modalElim k f (.modalIntro k' a)` β-reduces only
when k = k' (kind-discrimination preserves cross-kind correctness even
if typing is bypassed); cross-kind case produces a marker neutral.

Modal.lean transient alias block (top of file, outside namespace) for
backward dot-syntax reference (`.flatIntro a` resolves to
`.modalIntro .flat a` via abbrev).  Phase 3 will rewrite Modal.lean
properly to use the unified constructors directly + forModalityKind-
derived functor.

Net: −145 lines across the cascade (-478 deletions, +333 insertions).

Build: lake build (48 jobs) + lake build CubicalTransport (43 jobs) PASS.
Runtime: lake exe cubical-test 49/49 + 46/46 = 95/95 PASS.
Sorry count: Modal.lean 3 (unchanged), total engine 33 (no new sorries
from this phase, all annotated).

The Rust ABI v6 still uses 9 modal tags — diverges from the Lean side
after this commit but FFI tests don't exercise modal paths so no
runtime regression.  Phase 4 will sync to ABI v7.

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

194 lines
8.5 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.DecEq
======================
Decidable equality for the 5-way mutual block (`CType`, `CTerm`,
`CTypeArg`, `CtorSpec`, `CTypeSchema`) plus the list/pair helper
shapes that appear inside it.
Lean 4's `deriving instance DecidableEq` does not currently support
mutual inductives — has to be written manually.
## Universe-aware shape
`CType` is `CType : ULevel → Type`. Most CType constructors with sub-
CType payloads keep their sub-components at the same level as the
outer type (`path`, `glue`, `lift`, `interval`, `univ`, `ind`). But
`pi` and `sigma` carry sub-components at potentially distinct levels
`A, B` — only their `max` is fixed by the index.
Cross-level decidable equality is genuinely tricky in Lean's type
theory (an indexed-family `cases hA : HEq A A'` does not give us
`A = A'` without injectivity-of-the-index, which Lean doesn't ship
for arbitrary indexed inductives). We therefore route everything
through a level-erased `Σ : ULevel, CType ` boolean equality and
expose only the boolean workhorses (`beqCTypeAny`, `beqCTerm`, etc.)
for downstream consumers.
These workhorses are *computable* — they use the `partial def`
structure of the mutual block and dispatch by constructor pattern.
Used by `CubicalTransport.Question` for the syntactic-classifier
predicates (`IsTransport q := CTerm.beq q.u q.t = true`) and by
the Rust FFI bridge for cross-language equality checks.
-/
import CubicalTransport.Syntax
namespace CubicalTransport.DecEq
-- ── Boolean equality on level-erased CType ─────────────────────────────────
-- Single workhorse: compares Σ-pairs. Sub-component CTypes are also
-- compared as Σ-pairs, sidestepping any cross-level pattern issues.
mutual
partial def beqCTypeAny : (Σ : ULevel, CType ) → (Σ : ULevel, CType ) → Bool
| ⟨_, .univ ( := )⟩, ⟨_, .univ ( := ')⟩ => decide ( = ')
| ⟨_, .pi var A B⟩, ⟨_, .pi var' A' B'⟩ =>
var == var' && beqCTypeAny ⟨_, A⟩ ⟨_, A'⟩ && beqCTypeAny ⟨_, B⟩ ⟨_, B'⟩
| ⟨_, .sigma var A B⟩, ⟨_, .sigma var' A' B'⟩ =>
var == var' && beqCTypeAny ⟨_, A⟩ ⟨_, A'⟩ && beqCTypeAny ⟨_, B⟩ ⟨_, B'⟩
| ⟨_, .path A a b⟩, ⟨_, .path A' a' b'⟩ =>
beqCTypeAny ⟨_, A⟩ ⟨_, A'⟩ && beqCTerm a a' && beqCTerm b b'
| ⟨_, .glue ψ T f fInv s r c A⟩, ⟨_, .glue ψ' T' f' fInv' s' r' c' A'⟩ =>
ψ == ψ' && beqCTypeAny ⟨_, T⟩ ⟨_, T'⟩ &&
beqCTerm f f' && beqCTerm fInv fInv' &&
beqCTerm s s' && beqCTerm r r' && beqCTerm c c' &&
beqCTypeAny ⟨_, A⟩ ⟨_, A'⟩
| ⟨_, .ind ( := ) S ps⟩, ⟨_, .ind ( := ') S' ps'⟩ =>
decide ( = ') && beqCTypeSchema S S' && beqParams ps ps'
| ⟨_, .interval⟩, ⟨_, .interval⟩ => true
| ⟨_, .lift A⟩, ⟨_, .lift A'⟩ =>
beqCTypeAny ⟨_, A⟩ ⟨_, A'⟩
| ⟨_, .El P⟩, ⟨_, .El Q⟩ =>
beqCTerm P Q
| ⟨_, .modal k A⟩, ⟨_, .modal k' B⟩ =>
decide (k = k') && beqCTypeAny ⟨_, A⟩ ⟨_, B⟩
| _, _ => false
partial def beqCTerm : CTerm → CTerm → Bool
| .var x, .var y => x == y
| .lam x t, .lam y u => x == y && beqCTerm t u
| .app f a, .app f' a' => beqCTerm f f' && beqCTerm a a'
| .plam i t, .plam j u => i == j && beqCTerm t u
| .papp t r, .papp u s => r == s && beqCTerm t u
| .transp i A φ t, .transp j B ψ u =>
i == j && φ == ψ && beqCTypeAny ⟨_, A⟩ ⟨_, B⟩ && beqCTerm t u
| .comp i A φ u t, .comp j B ψ u' t' =>
i == j && φ == ψ && beqCTypeAny ⟨_, A⟩ ⟨_, B⟩ &&
beqCTerm u u' && beqCTerm t t'
| .compN i A cs t, .compN j B cs' t' =>
i == j && beqCTypeAny ⟨_, A⟩ ⟨_, B⟩ &&
beqClauses cs cs' && beqCTerm t t'
| .glueIn φ t a, .glueIn ψ u b =>
φ == ψ && beqCTerm t u && beqCTerm a b
| .unglue φ f g, .unglue ψ f' g' =>
φ == ψ && beqCTerm f f' && beqCTerm g g'
| .pair a b, .pair a' b' => beqCTerm a a' && beqCTerm b b'
| .fst t, .fst u => beqCTerm t u
| .snd t, .snd u => beqCTerm t u
| .dimExpr r, .dimExpr s => r == s
| .ctor S c ps as, .ctor S' c' ps' as' =>
c == c' && beqCTypeSchema S S' && beqParams ps ps' && beqList as as'
| .indElim S ps m bs t, .indElim S' ps' m' bs' t' =>
beqCTypeSchema S S' && beqParams ps ps' &&
beqCTerm m m' && beqBranches bs bs' && beqCTerm t t'
| .code A, .code B =>
-- A and B may live at different universe levels. Route through
-- the level-erased Σ-pair beq to compare them honestly.
beqCTypeAny ⟨_, A⟩ ⟨_, B⟩
-- Modal introduction: structural equality on (kind, wrapped term).
| .modalIntro k a, .modalIntro k' b =>
decide (k = k') && beqCTerm a b
-- Modal elimination: structural equality on (kind, eliminator, scrutinee).
| .modalElim k f m, .modalElim k' f' m' =>
decide (k = k') && beqCTerm f f' && beqCTerm m m'
| _, _ => false
partial def beqCTypeArg : CTypeArg → CTypeArg → Bool
| .type A, .type B => beqCTypeAny ⟨_, A⟩ ⟨_, B⟩
| .param i, .param j => i == j
| .self, .self => true
| .dim, .dim => true
| _, _ => false
partial def beqCtorSpec : CtorSpec → CtorSpec → Bool
| .mk n as bs, .mk n' as' bs' =>
n == n' && beqArgList as as' && beqClauses bs bs'
partial def beqCTypeSchema : CTypeSchema → CTypeSchema → Bool
| .mk n np cs, .mk n' np' cs' =>
n == n' && np == np' && beqCtorList cs cs'
-- ── List / clause / branch helpers ──────────────────────────────────────────
partial def beqParams : List (Σ : ULevel, CType ) → List (Σ : ULevel, CType ) → Bool
| [], [] => true
| x :: xs, y :: ys => beqCTypeAny x y && beqParams xs ys
| _, _ => false
partial def beqList : List CTerm → List CTerm → Bool
| [], [] => true
| x :: xs, y :: ys => beqCTerm x y && beqList xs ys
| _, _ => false
partial def beqArgList : List CTypeArg → List CTypeArg → Bool
| [], [] => true
| x :: xs, y :: ys => beqCTypeArg x y && beqArgList xs ys
| _, _ => false
partial def beqCtorList : List CtorSpec → List CtorSpec → Bool
| [], [] => true
| x :: xs, y :: ys => beqCtorSpec x y && beqCtorList xs ys
| _, _ => false
partial def beqClauses : List (FaceFormula × CTerm) → List (FaceFormula × CTerm) → Bool
| [], [] => true
| (φ, t) :: xs, (ψ, u) :: ys =>
φ == ψ && beqCTerm t u && beqClauses xs ys
| _, _ => false
partial def beqBranches : List (String × CTerm) → List (String × CTerm) → Bool
| [], [] => true
| (n, t) :: xs, (n', u) :: ys =>
n == n' && beqCTerm t u && beqBranches xs ys
| _, _ => false
end
-- ── Same-level CType beq derived from Σ-level beq ──────────────────────────
/-- Same-level boolean equality for `CType `. -/
def CType.beq { : ULevel} (a b : CType ) : Bool :=
beqCTypeAny ⟨ℓ, a⟩ ⟨ℓ, b⟩
/-- Same-level boolean equality for CTerm. -/
def CTerm.beq (a b : CTerm) : Bool := beqCTerm a b
/-- Boolean equality for CTypeArg. -/
def CTypeArg.beq (a b : CTypeArg) : Bool := beqCTypeArg a b
/-- Boolean equality for CtorSpec. -/
def CtorSpec.beq (a b : CtorSpec) : Bool := beqCtorSpec a b
/-- Boolean equality for CTypeSchema. -/
def CTypeSchema.beq (a b : CTypeSchema) : Bool := beqCTypeSchema a b
-- ── Decidable equality ─────────────────────────────────────────────────────
-- We do NOT provide `DecidableEq` instances for the mutual block. The
-- universe-stratified `CType : ULevel → Type` has cross-level pi/sigma
-- sub-components, which would force the DecEq mutual block to handle
-- HEq elimination across distinct universe indices — which is not
-- available in Lean 4 without K.
--
-- Consumers that need to decide equality on the cubical syntax should
-- use the boolean `beq`/`beqCTypeAny` workhorses above, which ARE
-- computable. These are the routes used by `Question.lean`'s
-- classifiers and the Rust FFI bridge.
--
-- (Previously these instances were defined as non-computable
-- Classical fallbacks, but that was a stratification leak: the
-- engine is constructive cubical, and Classical reasoning is a
-- foundational change to its discipline. The boolean `beq` route is
-- the structural alternative.)
end CubicalTransport.DecEq