/- Topolei.Cubical.Syntax ====================== Deep embedding of the cubical term language (CCHM §2–3). Grammar: A, B ::= U | Π A B | Path A a b t, u ::= x | λx.t | t u | ⟨i⟩t | t@r | transpⁱ A φ t | compⁱ A φ u t CType and CTerm are mutually inductive because path types carry endpoint terms, and terms carry path applications over DimExprs. transp and comp store (i : DimVar) (A : CType) inline rather than DimLine, because DimLine is defined later in the import chain (Subst → DimLine). The typing rules in Typing.lean use DimLine as a convenient wrapper. The path β-rule (⟨i⟩ t) @ r ↝ t[i := r] and the four "fully-reducing" transport/comp cases (T1, T2, C1, C2) are NbE theorems in `Cubical/Readback.lean`. The residual step-level axioms — T3, T5, C4 (subject reduction + face congruence) and T4 (path-line shape preservation) — live in `TransportLaws.lean` / `CompLaws.lean`. -/ import CubicalTransport.Face -- ── Syntax ──────────────────────────────────────────────────────────────────── mutual /-- Types in the cubical calculus. -/ inductive CType where | univ : CType -- U | pi (A : CType) (B : CType) : CType -- Π A B | path (A : CType) (a b : CTerm) : CType -- Path A a b /-- Non-dependent Σ type (cells-spec §6.2, §8.1, §9.2). `Sigma A B` — pairs whose first component has type `A` and whose second has type `B`. Non-dependent: `B` does not refer to the first component. Dependent Σ (where `B : A → CType`) is deferred for the same reason as dependent Π — requires a term evaluator to apply `B` to a term. -/ | sigma (A B : CType) : CType -- Σ A B /-- Glue type (CCHM §6). `Glue [φ ↦ (T, e)] A` — on face `φ` the type is `T` with `e : T ≃ A` as witness; off face `φ` the type is `A`. The equivalence `e` is inlined as five `CTerm` fields (f, fInv, sec, ret, coh) rather than a nested `EquivData` so that `CType` / `CTerm` remain a closed mutual inductive block — `EquivData` lives in `Equiv.lean` and is downstream in the import chain. Use `EquivData.toGlueType` in `Equiv.lean` for an ergonomic wrapper. -/ | glue (φ : FaceFormula) (T : CType) (f fInv sec ret coh : CTerm) (A : CType) : CType -- Glue [φ ↦ (T, e)] A deriving Repr /-- Terms in the cubical calculus. -/ inductive CTerm where | var (x : String) : CTerm -- x | lam (x : String) (t : CTerm) : CTerm -- λx. t | app (f a : CTerm) : CTerm -- f a | plam (i : DimVar) (t : CTerm) : CTerm -- ⟨i⟩ t | papp (t : CTerm) (r : DimExpr) : CTerm -- t @ r | transp (i : DimVar) (A : CType) (φ : FaceFormula) (t : CTerm) : CTerm -- transpⁱ A φ t | comp (i : DimVar) (A : CType) (φ : FaceFormula) (u t : CTerm) : CTerm -- compⁱ A φ u t /-- Multi-clause heterogeneous composition. `compⁱ A [φ₁ ↦ u₁, …, φₙ ↦ uₙ] t` — a partial element defined over the union of the clause faces. Coherence (clauses agree on overlaps and with `t` at `i = 0`) is a typing-level side condition, not enforced syntactically. Used by CCHM path transport and heterogeneous Π composition to express systems that a single-clause `.comp` cannot represent. -/ | compN (i : DimVar) (A : CType) (clauses : List (FaceFormula × CTerm)) (t : CTerm) : CTerm -- compⁱ A [φ₁↦u₁,…] t /-- Glue introduction (CCHM §6.1). `glueIn [φ ↦ t] a` — on face `φ`, equals `t : T`; off face `φ`, equals `a : A`. Well-typedness requires `e.f t = a` on the overlap (a typing-side condition, not enforced syntactically). The equivalence `e` is carried by the type, not the term. -/ | glueIn (φ : FaceFormula) (t a : CTerm) : CTerm -- glue [φ↦t] a /-- Glue elimination (CCHM §6.1). `unglue [φ ↦ f] g` — extract the underlying `A`-value from a glued term. On face `φ`, equals `f g` (apply the forward map); off face `φ`, equals `g` (already an `A`-value). `f` is the `f`-field of the equivalence witnessing `T ≃ A` — carried explicitly at the term level because we don't have type annotations on terms. -/ | unglue (φ : FaceFormula) (f g : CTerm) : CTerm -- unglue [φ↦f] g /-- Σ introduction (pair). -/ | pair (a b : CTerm) : CTerm -- (a, b) /-- Σ elimination (first projection). -/ | fst (t : CTerm) : CTerm -- t.1 /-- Σ elimination (second projection). -/ | snd (t : CTerm) : CTerm -- t.2 deriving Repr end -- ── Dimension substitution ──────────────────────────────────────────────────── -- Substitute dimension variable i with DimExpr r throughout a term. -- -- Scope inside transp/comp: -- · j is the binder of the transport line, bound in A and in φ. -- · The base term t (and system u) are in outer scope — we substitute there. -- -- Approximation: `substDim` does NOT descend into A or φ — even when j ≠ i -- and i would be free under the binder. Consequence: this substitution is -- only faithful for *endpoint* calls (`substDimBool`), where downstream -- uses the dimension-absent predicate to justify correctness. Full -- DimExpr-in-FaceFormula substitution is deferred (see cells-spec §5.5). mutual def CTerm.substDim (i : DimVar) (r : DimExpr) : CTerm → CTerm | .var x => .var x | .lam x t => .lam x (t.substDim i r) | .app f a => .app (f.substDim i r) (a.substDim i r) | .plam j t => if j = i then .plam j t -- i bound; stop else .plam j (t.substDim i r) | .papp t s => .papp (t.substDim i r) (DimExpr.subst i r s) -- transp/comp: leave A alone (approximation); descend into t, u and -- substitute in φ via the general DimExpr face-formula substitution. | .transp j A φ t => .transp j A (φ.substDim i r) (t.substDim i r) | .comp j A φ u t => .comp j A (φ.substDim i r) (u.substDim i r) (t.substDim i r) -- Multi-clause comp: substitute in each clause's face and body, and in t. -- Uses an explicit recursive helper `substDim.clauses` so Lean can see -- structural termination through the clause list. | .compN j A clauses t => .compN j A (CTerm.substDim.clauses i r clauses) (t.substDim i r) -- Glue introduction / elimination: descend into all sub-terms and -- substitute into the face formula. Same approximation for types -- as transp/comp (A not touched — the `φ` face formula carries the -- whole dim-dependency story; in our approximation we still don't -- recurse into CType sub-trees here, matching `.transp`/`.comp`). | .glueIn φ t a => .glueIn (φ.substDim i r) (t.substDim i r) (a.substDim i r) | .unglue φ f g => .unglue (φ.substDim i r) (f.substDim i r) (g.substDim i r) -- Σ constructors: structural recursion into sub-terms. | .pair a b => .pair (a.substDim i r) (b.substDim i r) | .fst t => .fst (t.substDim i r) | .snd t => .snd (t.substDim i r) /-- Helper: apply `CTerm.substDim i r` to each clause body (and `FaceFormula.substDim` to each face) in a system's clause list. Defined mutually with `substDim` so Lean can verify termination. -/ def CTerm.substDim.clauses (i : DimVar) (r : DimExpr) : List (FaceFormula × CTerm) → List (FaceFormula × CTerm) | [] => [] | (φ, u) :: rest => (φ.substDim i r, u.substDim i r) :: CTerm.substDim.clauses i r rest end -- ── One-step reduction ──────────────────────────────────────────────────────── -- `CTerm.step` is left *opaque*. Semantically it is what a CCHM-style -- evaluator will compute. Keeping `step` opaque is required for -- soundness: if `step` were a concrete `def` with a wildcard identity -- arm, any axiom of the shape `step (.transp …) = t` would rfl-collapse -- to `.transp … = t`, contradicting `CTerm.noConfusion`. -- -- **Stage 4.4 decision (2026-04-23).** After the Phase 1 step↔eval -- bridge + Stream B #2d + Stage 2.3, only one step-level axiom remains: -- `transp_plam_is_plam_path` (T4, path-restricted form; Stage 4.2). -- T1/T2/C1/C2/T5/`step_papp_plam` are NbE theorems in `Readback.lean`; -- T3/C4 are theorems via `CTerm.step_preserves_type` (ValueTyping.lean); -- glue β/η are theorems via `eval_unglue_of_glueIn`/`eval_glueIn_of_unglue`. -- -- **Rust discharge flexibility.** The Rust backend has two valid -- implementation strategies for `CTerm.step`: -- -- · *Option A — native step*: implement `step` directly as a C ABI -- entry point. The single T4 axiom is discharged by emitting the -- CCHM comp-shaped body for path-typed transp-of-plam inputs, plus -- identity behaviour on other shapes. -- -- · *Option B — derived step*: define `step := readback ∘ eval .nil` -- entirely in Rust (no separate `step` FFI entry). This matches -- the Lean-side "bridge" intuition and keeps the Rust FFI surface -- smaller by one function. Satisfies T4 on path-typed lines via -- the `vPathTransp` → `.compN` readback arm. -- -- The Lean-side spec is agnostic to which strategy Rust picks — both -- satisfy the same axiom set. See FFI_DESIGN.md (Stage 4.5) for the -- concrete recommendation. opaque CTerm.step : CTerm → CTerm := id