Adds schema-based inductive type encoding to the engine, supporting both
plain inductives (Nat, List, Bool) and HITs (S¹, propositional truncation,
suspensions) via a single CTypeSchema/CtorSpec/CTypeArg structure.
New CType constructor:
- .ind (S : CTypeSchema) (params : List CType)
New CTerm constructors:
- .dimExpr (r : DimExpr)
- .ctor (S, ctorName, params, args)
- .indElim (S, params, motive, branches, target)
New CVal constructors:
- .vctor (S, ctorName, params, evaluatedArgs)
- .vdimExpr (DimExpr)
New CNeu constructor:
- .nIndElim (S, params, motive, branches, neuTarget)
Five-way mutual block in Syntax.lean (CType, CTerm, CTypeArg, CtorSpec,
CTypeSchema) with Repr derivation post-hoc. Tag layout per
docs/INDUCTIVE_TYPES.md §6: old tags preserved (no shifting). Existing
62/62 smoke + property tests pass unchanged through Rust.
Substitution / dim-absent / endpoint handling:
Subst.lean — substDim, substDimExpr, equation lemmas,
substDim_eq_substDimExpr (mutual with .params helpers).
DimLine.lean — CTerm.dimAbsent + CType.dimAbsent (mutual w/ helpers
for list / branches / params). Plus all five
auxiliary mutual blocks: substDim_absent_aux,
substDimExpr_absent_aux, dimAbsent_after_substDim_aux,
substDim_comm_aux — for both CTerm and CType.
Eval.lean: ctor → vctor (eval'd args); indElim β-reduces on canonical
vctor target via vApp chain over branch body, otherwise stuck via
.nIndElim; dimExpr → vdimExpr. Path-ctor boundary firing and
recursive-arg IH wiring marked TODO REL1.1 (β-reduction works for
non-recursive ctors today).
Readback.lean: vctor → .ctor, vdimExpr → .dimExpr, nIndElim → .indElim.
FFITest.lean: cvalSummary / ctermSummary extended with new constructor
arms.
Topolei (sibling repo) has not yet been migrated — see
docs/INDUCTIVE_TYPES.md §9.1 for the per-file impact map.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
821 lines
38 KiB
Text
821 lines
38 KiB
Text
/-
|
||
Topolei.Cubical.Eval
|
||
====================
|
||
Environment-based evaluator for the cubical calculus (cells-spec §5.4,
|
||
Phase 1 Week 2).
|
||
|
||
`eval env t` reduces `t` to weak-head normal form in environment `env`.
|
||
Three mutually-recursive pieces:
|
||
|
||
· `eval` : `CEnv → CTerm → CVal` — main evaluator
|
||
· `vApp` : `CVal → CVal → CVal` — function application at the value level
|
||
· `vPApp` : `CVal → DimExpr → CVal` — path (dimension) application at value level
|
||
|
||
Coverage: the *λ-calculus fragment* — `var`/`lam`/`app`/`plam`/`papp`.
|
||
`transp`/`comp` produce stuck neutrals (`ntransp`/`ncomp`); their real
|
||
reduction rules come with `Transport.lean` and `Comp.lean` (Weeks 3–4).
|
||
|
||
These are `partial def`s. Termination isn't statically checked because the
|
||
`vPApp` β-step substitutes inside the closure body and re-evaluates; the
|
||
result is the same `CTerm` size, but Lean's structural recursion can't see
|
||
through that. A future total version will measure on a subject-reduction
|
||
metric. For now, `partial def` is the honest choice.
|
||
-/
|
||
|
||
import CubicalTransport.Value
|
||
import CubicalTransport.Transport
|
||
|
||
-- ── Rust FFI declarations (Phase C.2) ──────────────────────────────────────
|
||
-- `@[extern "topolei_cubical_*"] opaque *Rust ...` declares the Rust
|
||
-- entry point. `@[implemented_by]` on each partial def routes runtime
|
||
-- calls to Rust (kernel-level proof reasoning still uses the axioms).
|
||
|
||
@[extern "topolei_cubical_eval"]
|
||
opaque evalRust (env : CEnv) : CTerm → CVal
|
||
|
||
@[extern "topolei_cubical_vapp"]
|
||
opaque vAppRust : CVal → CVal → CVal
|
||
|
||
@[extern "topolei_cubical_vpapp"]
|
||
opaque vPAppRust : CVal → DimExpr → CVal
|
||
|
||
@[extern "topolei_cubical_vhcomp"]
|
||
opaque vHCompValueRust (A : CType) (φ : FaceFormula) (tube base : CVal) : CVal
|
||
|
||
@[extern "topolei_cubical_vcomp_term"]
|
||
opaque vCompAtTermRust (env : CEnv) (i : DimVar) (A : CType)
|
||
(φ : FaceFormula) (u t : CTerm) : CVal
|
||
|
||
@[extern "topolei_cubical_vcompn_term"]
|
||
opaque vCompNAtTermRust (env : CEnv) (i : DimVar) (A : CType)
|
||
(clauses : List (FaceFormula × CTerm)) (t : CTerm) : CVal
|
||
|
||
@[extern "topolei_cubical_vfst"]
|
||
opaque vFstRust : CVal → CVal
|
||
|
||
@[extern "topolei_cubical_vsnd"]
|
||
opaque vSndRust : CVal → CVal
|
||
|
||
mutual
|
||
/-- Evaluate a term in an environment. λ-fragment + dimension abstraction
|
||
are reduced immediately; transport dispatches through `vTransp`;
|
||
heterogeneous composition dispatches through `vCompAtTerm` (which has
|
||
access to the raw CTerms so it can substitute at the term level for
|
||
`comp_full`).
|
||
|
||
This is a `partial def` because the β-step in `vPApp` substitutes inside
|
||
a closure body (via `CTerm.substDim`) and re-evaluates — the resulting
|
||
term is the same CTerm size, but Lean's structural-recursion checker
|
||
can't see through that, and indeed an untyped Ω-like term genuinely
|
||
diverges. A well-typed total evaluator arrives in a later phase. -/
|
||
@[implemented_by evalRust]
|
||
partial def eval (env : CEnv) : CTerm → CVal
|
||
| .var x => (env.lookup x).getD (.vneu (.nvar x))
|
||
| .lam x body => .vlam env x body
|
||
| .app f a => vApp (eval env f) (eval env a)
|
||
| .plam i body => .vplam env i body
|
||
| .papp t r => vPApp (eval env t) r
|
||
| .transp i A φ t =>
|
||
-- Priority order: (1) full face, (2) constant line, (3) path
|
||
-- (needs env), (4) glue (dispatched via dedicated axioms in
|
||
-- `Glue.lean`; the partial def produces a stuck neutral as a
|
||
-- runtime placeholder), (5) other via value-level `vTransp`.
|
||
match φ with
|
||
| .top => eval env t -- (1) T1
|
||
| _ =>
|
||
if CType.dimAbsent i A then
|
||
eval env t -- (2) T2
|
||
else
|
||
match A with
|
||
| .path A₀ a b => .vPathTransp env i A₀ a b φ t -- (3)
|
||
| .glue _ _ _ _ _ _ _ _ => -- (4) Glue
|
||
-- Real reduction rules live in `Glue.lean` as dedicated
|
||
-- axioms keyed on specific sub-cases (e.g. constant
|
||
-- equivalence components). The partial def produces
|
||
-- `ntransp` as a placeholder — the same shape vTransp
|
||
-- would produce on stuck glue — so that kernel-level
|
||
-- proofs go exclusively through the Glue axioms.
|
||
.vneu (.ntransp i A φ (eval env t))
|
||
| _ => vTransp i A φ (eval env t) -- (5)
|
||
| .comp i A φ u t => vCompAtTerm env i A φ u t
|
||
| .compN i A clauses t => vCompNAtTerm env i A clauses t
|
||
| .glueIn φ t a =>
|
||
-- Face-priority dispatch: φ = .top → t-side, φ = .bot → a-side,
|
||
-- else stuck `.nglueIn` preserving both face-on / face-off values.
|
||
match φ with
|
||
| .top => eval env t
|
||
| .bot => eval env a
|
||
| _ => .vneu (.nglueIn φ (eval env t) (eval env a))
|
||
| .unglue φ f g =>
|
||
-- Face-priority dispatch:
|
||
-- φ = .top: apply the forward map; the argument is a T-value.
|
||
-- φ = .bot: identity — on the empty face, the glued term is the
|
||
-- A-value `g` itself (consistent with `glueIn .bot t a → a`).
|
||
-- Else: stuck `.nunglue` preserving `f` and `g`.
|
||
match φ with
|
||
| .top => vApp (eval env f) (eval env g)
|
||
| .bot => eval env g
|
||
| _ => .vneu (.nunglue φ (eval env f) (eval env g))
|
||
| .pair a b => .vpair (eval env a) (eval env b)
|
||
| .fst t => vFst (eval env t)
|
||
| .snd t => vSnd (eval env t)
|
||
-- REL1 inductive-type constructors.
|
||
| .dimExpr r => .vdimExpr r
|
||
| .ctor S c params args =>
|
||
-- Produce a canonical constructor value with all args evaluated.
|
||
-- (Boundary firing for path ctors lands in a follow-up — REL1
|
||
-- design doc §4: when a `.dim`-typed arg evaluates to .zero/.one
|
||
-- and matches a clause in `S.ctors[c].boundary`, we should fire
|
||
-- that clause body instead. TODO REL1.1.)
|
||
.vctor S c params (args.map (eval env))
|
||
| .indElim S params motive branches target =>
|
||
-- β-reduce on a canonical `vctor`; otherwise build a stuck
|
||
-- `.nIndElim` that preserves env-evaluated motive and branches
|
||
-- so it can unstick when target later resolves.
|
||
match eval env target with
|
||
| .vctor _ c _ argsV =>
|
||
match List.lookup c branches with
|
||
| some body =>
|
||
-- Apply branch body to constructor's args via repeated
|
||
-- `vApp` — the body is curried: λ a₁ … aₙ. result.
|
||
-- Recursive-arg hypotheses (the IH passed alongside each
|
||
-- `.self` arg in the design doc §5) are not yet wired
|
||
-- here; an `indElim` of a non-recursive ctor is fully
|
||
-- handled, but recursive ctors land their branch body
|
||
-- with only the constructor args (no IH) — TODO REL1.1.
|
||
argsV.foldl (fun f a => vApp f a) (eval env body)
|
||
| none =>
|
||
.vneu (.nvar s!"<indElim: no branch for {c}>")
|
||
| .vneu n =>
|
||
.vneu (.nIndElim S params (eval env motive)
|
||
(branches.map (fun (nm, b) => (nm, eval env b))) n)
|
||
| _ =>
|
||
.vneu (.nvar "<indElim: target is not canonical>")
|
||
|
||
/-- First projection at the value level. β-reduces `vpair`; pushes a
|
||
stuck neutral into `nfst`. Projecting any other value shape is a
|
||
type error (marker neutral). -/
|
||
@[implemented_by vFstRust]
|
||
partial def vFst : CVal → CVal
|
||
| .vpair a _ => a
|
||
| .vneu n => .vneu (.nfst n)
|
||
| _ => .vneu (.nvar "<vFst: non-pair projection>")
|
||
|
||
/-- Second projection at the value level. Mirror of `vFst`. -/
|
||
@[implemented_by vSndRust]
|
||
partial def vSnd : CVal → CVal
|
||
| .vpair _ b => b
|
||
| .vneu n => .vneu (.nsnd n)
|
||
| _ => .vneu (.nvar "<vSnd: non-pair projection>")
|
||
|
||
/-- Apply one value to another. β-reduces `vlam` closures; pushes stuck
|
||
neutrals into `napp`. `vTranspFun` unfolds per the *full* CCHM Π rule.
|
||
`vHCompFun` unfolds per the CCHM Π hcomp rule: apply pointwise
|
||
through the tube, then recursively hcomp on the codomain. Applying
|
||
a `vplam` or `vTubeApp` as a function is a type error. -/
|
||
@[implemented_by vAppRust]
|
||
partial def vApp : CVal → CVal → CVal
|
||
| .vlam env x body, arg => eval (env.extend x arg) body
|
||
| .vneu n, arg => .vneu (.napp n arg)
|
||
| .vTranspFun i domA codA φ f, arg =>
|
||
vTransp i codA φ (vApp f (vTranspInv i domA φ arg))
|
||
| .vHCompFun codA φ tube base, arg =>
|
||
vHCompValue codA φ (.vTubeApp tube arg) (vApp base arg)
|
||
-- Full CCHM Π hetero comp β-rule. See file-level comment below for
|
||
-- the fill construction.
|
||
| .vCompFun env i domA codA φ u t, arg =>
|
||
let yName := "$y"
|
||
let fj : DimVar := ⟨"$fj"⟩
|
||
let newEnv := env.extend yName arg
|
||
-- `filled_y_at_i`: a line in the *outer* binder `i` giving y_at_i:A(i).
|
||
-- Build as `transp^{fj} (domA[i := (inv fj) ∨ i]) φ ($y)`:
|
||
-- at fj=0: line slice is A(1) (source: y : A(1))
|
||
-- at fj=1: line slice is A(i) (target we want)
|
||
let filled_y_at_i : CTerm :=
|
||
.transp fj
|
||
(domA.substDimExpr i (.join (.inv (.var fj)) (.var i)))
|
||
φ (.var yName)
|
||
-- `filled_y_at_0`: substitute i := 0 in the above — the reversed
|
||
-- line is `A[i := inv fj]`, taking A(1) → A(0).
|
||
let filled_y_at_0 : CTerm :=
|
||
.transp fj
|
||
(domA.substDimExpr i (.inv (.var fj)))
|
||
φ (.var yName)
|
||
eval newEnv (.comp i codA φ
|
||
(.app u filled_y_at_i)
|
||
(.app t filled_y_at_0))
|
||
| .vplam _ _ _, _ => .vneu (.nvar "<vApp: vplam applied as function>")
|
||
| .vTubeApp _ _, _ => .vneu (.nvar "<vApp: vTubeApp applied as function>")
|
||
| .vPathTransp _ _ _ _ _ _ _, _ => .vneu (.nvar "<vApp: vPathTransp applied as function>")
|
||
| .vpair _ _, _ => .vneu (.nvar "<vApp: vpair applied as function>")
|
||
| .vctor _ _ _ _, _ => .vneu (.nvar "<vApp: vctor applied as function>")
|
||
| .vdimExpr _, _ => .vneu (.nvar "<vApp: vdimExpr applied as function>")
|
||
|
||
/-- Apply a value to a dimension expression. β-reduces `vplam` closures
|
||
by substituting the dim in the body and re-evaluating; pushes stuck
|
||
neutrals into `npapp`. `vTubeApp tube arg` applied to dim `r` reduces
|
||
to `vApp (vPApp tube r) arg` — the semantic meaning of
|
||
`λj. (tube @ j) arg`. Marker neutrals for ill-typed applications. -/
|
||
@[implemented_by vPAppRust]
|
||
partial def vPApp : CVal → DimExpr → CVal
|
||
| .vplam env i body, r => eval env (body.substDim i r)
|
||
| .vneu n, r => .vneu (.npapp n r)
|
||
| .vTubeApp tube arg, r => vApp (vPApp tube r) arg
|
||
-- Path transport endpoint reductions: at `.zero` / `.one` the CCHM
|
||
-- multi-clause system degenerates (the (j=0) or (j=1) clause fires)
|
||
-- and the result is the specified endpoint at i=1. At generic `r`
|
||
-- we fall through to `vCompNAtTerm` on the CCHM multi-clause system
|
||
-- `[φ ↦ p@r, (r=0) ↦ a, (r=1) ↦ b]` with base `p@r` — which reduces
|
||
-- further if the face substitutions happen to simplify.
|
||
| .vPathTransp env i _ a _ _ _, .zero => eval env (a.substDim i .one)
|
||
| .vPathTransp env i _ _ b _ _, .one => eval env (b.substDim i .one)
|
||
| .vPathTransp env i A a b φ p, r =>
|
||
vCompNAtTerm env i A
|
||
[ (φ, .papp p r)
|
||
, (FaceFormula.dimExprEq0 r, a)
|
||
, (FaceFormula.dimExprEq1 r, b) ]
|
||
(.papp p r)
|
||
| .vlam _ _ _, _ => .vneu (.nvar "<vPApp: vlam applied as path>")
|
||
| .vTranspFun _ _ _ _ _, _ => .vneu (.nvar "<vPApp: vTranspFun applied as path>")
|
||
| .vHCompFun _ _ _ _, _ => .vneu (.nvar "<vPApp: vHCompFun applied as path>")
|
||
| .vCompFun _ _ _ _ _ _ _, _ => .vneu (.nvar "<vPApp: vCompFun applied as path>")
|
||
| .vpair _ _, _ => .vneu (.nvar "<vPApp: vpair applied as path>")
|
||
| .vctor _ _ _ _, _ => .vneu (.nvar "<vPApp: vctor applied as path>")
|
||
| .vdimExpr _, _ => .vneu (.nvar "<vPApp: vdimExpr applied as path>")
|
||
|
||
/-- Homogeneous composition at the value level. The type `A` is
|
||
*homogeneous* (doesn't vary along `i`); the tube and base are
|
||
already-evaluated values.
|
||
|
||
Reducing cases (in match order):
|
||
1. `φ = .top` — tube covers everything, result is `tube @ 1`.
|
||
2. `A = .pi` — CCHM Π hcomp: construct a `vHCompFun` closure
|
||
that applies pointwise when the function is
|
||
applied to an argument.
|
||
3. Otherwise — stuck `.vneu (.nhcomp ...)`.
|
||
|
||
Note the crucial difference from `vTransp`: no constant-line check,
|
||
because hcomp is *already* homogeneous — constancy is built in. -/
|
||
@[implemented_by vHCompValueRust]
|
||
partial def vHCompValue (A : CType) (φ : FaceFormula) (tube base : CVal) :
|
||
CVal :=
|
||
match φ with
|
||
| .top => vPApp tube .one
|
||
| _ =>
|
||
match A with
|
||
| .pi _domA codA => .vHCompFun codA φ tube base
|
||
| _ => .vneu (.nhcomp A φ tube base)
|
||
|
||
/-- Heterogeneous composition at the term level. Takes `u` and `t` as
|
||
`CTerm`s (not `CVal`s) so that the `comp_full` reduction can perform
|
||
substitution *at the term level* — `eval env (u.substDim i .one)` —
|
||
which is in general different from `vPApp (eval env u) .one`.
|
||
|
||
Reducing cases (in match order):
|
||
1. `φ = .top` — C1: `eval env (u[i := 1])`.
|
||
2. `φ = .bot` — C2: `eval env (transp i A .bot t)`.
|
||
3. `CType.dimAbsent i A = true` — constant line: heterogeneous
|
||
comp reduces to hcomp on the
|
||
fixed type `A`, with `.plam i u`
|
||
as the tube.
|
||
4. Otherwise — stuck `.vneu (.ncomp ...)`.
|
||
|
||
Note cases (1) and (2) are decidable at the `.top`/`.bot` head of
|
||
`φ`; case (3) discriminates on `A` only after `.top`/`.bot` are
|
||
ruled out. All four cases are mutually exclusive. -/
|
||
@[implemented_by vCompAtTermRust]
|
||
partial def vCompAtTerm (env : CEnv) (i : DimVar) (A : CType)
|
||
(φ : FaceFormula) (u t : CTerm) : CVal :=
|
||
match φ with
|
||
| .top => eval env (u.substDim i .one)
|
||
| .bot => eval env (.transp i A .bot t)
|
||
| _ =>
|
||
if CType.dimAbsent i A then
|
||
-- Constant line: hetero comp reduces to hcomp. `.plam i u` is the
|
||
-- tube as a dim-closure.
|
||
vHCompValue A φ (eval env (.plam i u)) (eval env t)
|
||
else
|
||
match A with
|
||
| .pi domA codA =>
|
||
-- Hetero Π comp: package into a `vCompFun` closure. The CCHM
|
||
-- β-rule runs at `vApp`-time with a full fill-based tube.
|
||
.vCompFun env i domA codA φ u t
|
||
| _ =>
|
||
.vneu (.ncomp i A φ (eval env u) (eval env t))
|
||
|
||
/-- Multi-clause heterogeneous composition at the term level. Dispatches
|
||
by scanning the clause list:
|
||
· If any clause has face `.top`, that clause's body substituted at
|
||
`i := .one` is the result (CCHM multi-clause full-system rule).
|
||
· If all clause faces are `.bot` or the list is empty, no clause
|
||
fires — reduces to plain transport of the base.
|
||
· If exactly one clause has a non-trivial face, delegate to
|
||
`vCompAtTerm` (single-clause specialisation).
|
||
· Otherwise produce a stuck `ncompN` neutral preserving env, line
|
||
binder, type, evaluated clauses, and evaluated base. -/
|
||
@[implemented_by vCompNAtTermRust]
|
||
partial def vCompNAtTerm (env : CEnv) (i : DimVar) (A : CType)
|
||
(clauses : List (FaceFormula × CTerm)) (t : CTerm) : CVal :=
|
||
-- Scan for a `.top` clause first.
|
||
match clauses.find? (fun ⟨φ, _⟩ => match φ with | .top => true | _ => false) with
|
||
| some ⟨_, u⟩ => eval env (u.substDim i .one)
|
||
| none =>
|
||
-- Strip `.bot` clauses (they never fire).
|
||
let live := clauses.filter
|
||
(fun ⟨φ, _⟩ => match φ with | .bot => false | _ => true)
|
||
match live with
|
||
| [] => eval env (.transp i A .bot t)
|
||
| [⟨φ, u⟩] => vCompAtTerm env i A φ u t
|
||
| _ =>
|
||
.vneu (.ncompN env i A
|
||
(live.map (fun ⟨φ, u⟩ => (φ, eval env u)))
|
||
(eval env t))
|
||
end
|
||
|
||
/-!
|
||
## Reduction lemmas (axioms)
|
||
|
||
`partial def` is opaque at the kernel level, so the defining cases of
|
||
`eval`, `vApp`, and `vPApp` are not reducible by `rfl`. We state them as
|
||
axioms — the same pattern used for `CTerm.step` and `step_papp_plam` in
|
||
`Syntax.lean`. They exactly mirror the `partial def` match arms above,
|
||
so they are consistent with the runtime implementation while also being
|
||
usable in kernel-level proofs.
|
||
-/
|
||
|
||
-- Reduction lemmas for `eval`.
|
||
|
||
axiom eval_var (env : CEnv) (x : String) :
|
||
eval env (.var x) = (env.lookup x).getD (.vneu (.nvar x))
|
||
|
||
axiom eval_lam (env : CEnv) (x : String) (body : CTerm) :
|
||
eval env (.lam x body) = .vlam env x body
|
||
|
||
axiom eval_app (env : CEnv) (f a : CTerm) :
|
||
eval env (.app f a) = vApp (eval env f) (eval env a)
|
||
|
||
axiom eval_plam (env : CEnv) (i : DimVar) (body : CTerm) :
|
||
eval env (.plam i body) = .vplam env i body
|
||
|
||
axiom eval_papp (env : CEnv) (t : CTerm) (r : DimExpr) :
|
||
eval env (.papp t r) = vPApp (eval env t) r
|
||
|
||
/-!
|
||
### `eval` on `.transp` — four disjoint cases
|
||
|
||
Replaces the earlier coarse `eval_transp` axiom. Match-arm priority is:
|
||
1. `.top` face → identity (T1 at eval level).
|
||
2. Constant line → identity (T2 at eval level).
|
||
3. `.path A₀ a b` line, non-constant → `vPathTransp` closure.
|
||
4. Non-path, non-constant line → delegate to value-level `vTransp`.
|
||
The four cases are mutually exclusive by precondition, so the axiom set
|
||
is consistent. -/
|
||
|
||
/-- (1) T1 at eval level: transport under a full face is identity. -/
|
||
axiom eval_transp_top (env : CEnv) (i : DimVar) (A : CType) (t : CTerm) :
|
||
eval env (.transp i A .top t) = eval env t
|
||
|
||
/-- (2) T2 at eval level: transport along a constant line is identity.
|
||
Covers `.univ`, constant-`pi`, and constant-`path` lines uniformly. -/
|
||
axiom eval_transp_const (env : CEnv) (i : DimVar) (A : CType)
|
||
(φ : FaceFormula) (t : CTerm)
|
||
(hφ : φ ≠ .top)
|
||
(hA : CType.dimAbsent i A = true) :
|
||
eval env (.transp i A φ t) = eval env t
|
||
|
||
/-- (3) Path transport: when the line's body is `.path A₀ a b` with the
|
||
whole path-line genuinely varying, produce a `vPathTransp` closure
|
||
that captures the endpoint CTerms `a`/`b` along with the env and the
|
||
path term `t` (kept as a CTerm so later `.papp t r` constructions
|
||
work for the multi-clause reduction at generic dim). Reduces
|
||
further under `vPApp` at endpoints. -/
|
||
axiom eval_transp_path (env : CEnv) (i : DimVar) (A₀ : CType)
|
||
(a b : CTerm) (φ : FaceFormula) (t : CTerm)
|
||
(hφ : φ ≠ .top)
|
||
(hA : CType.dimAbsent i (.path A₀ a b) = false) :
|
||
eval env (.transp i (.path A₀ a b) φ t) =
|
||
.vPathTransp env i A₀ a b φ t
|
||
|
||
/-- (4) Non-path non-glue non-constant transport: delegate to the value-level
|
||
`vTransp`, which is env-agnostic and handles `.pi` via `vTranspFun`.
|
||
`.glue` is excluded because its CCHM transport formula lives in dedicated
|
||
Glue-specific axioms (see `Glue.lean`); routing it through `vTransp`
|
||
here would claim it reduces to a stuck neutral, which would contradict
|
||
those axioms in their specific sub-cases. -/
|
||
axiom eval_transp_nonpath (env : CEnv) (i : DimVar) (A : CType)
|
||
(φ : FaceFormula) (t : CTerm)
|
||
(hφ : φ ≠ .top)
|
||
(hA : CType.dimAbsent i A = false)
|
||
(h_not_path : ∀ A₀ a b, A ≠ .path A₀ a b)
|
||
(h_not_glue : ∀ φG T f fInv sec ret coh Ai,
|
||
A ≠ .glue φG T f fInv sec ret coh Ai) :
|
||
eval env (.transp i A φ t) = vTransp i A φ (eval env t)
|
||
|
||
/-- Π-case theorem (full CCHM): transport along any `pi domA codA` line
|
||
produces a `vTranspFun` closure. Derived via `eval_transp_nonpath`
|
||
(`pi ≠ path` and `pi ≠ glue` by constructor disjointness) plus
|
||
`vTransp_pi`. -/
|
||
theorem eval_transp_pi (env : CEnv) (i : DimVar)
|
||
(domA codA : CType) (φ : FaceFormula) (t : CTerm)
|
||
(hφ : φ ≠ .top)
|
||
(hA : CType.dimAbsent i (.pi domA codA) = false) :
|
||
eval env (.transp i (.pi domA codA) φ t) =
|
||
.vTranspFun i domA codA φ (eval env t) := by
|
||
rw [eval_transp_nonpath env i _ φ t hφ hA
|
||
(by intro _ _ _ h; nomatch h)
|
||
(by intro _ _ _ _ _ _ _ _ h; nomatch h)]
|
||
exact vTransp_pi _ _ _ _ _ hφ hA
|
||
|
||
/-- Stuck fallback theorem. In our current `CType` universe
|
||
`{univ, pi, path, glue}`, this never actually fires in practice: `univ`
|
||
always has `dimAbsent = true`, so the const case wins; `pi` is handled
|
||
by `eval_transp_pi`; `path` is handled by `eval_transp_path`; `glue` is
|
||
handled by the dedicated Glue-transport axioms in `Glue.lean`. Kept
|
||
here for parity with `vTransp_stuck` and future CType extensions. -/
|
||
theorem eval_transp_stuck (env : CEnv) (i : DimVar) (A : CType)
|
||
(φ : FaceFormula) (t : CTerm)
|
||
(hφ : φ ≠ .top)
|
||
(hA : CType.dimAbsent i A = false)
|
||
(h_not_pi : ∀ domA codA, A ≠ .pi domA codA)
|
||
(h_not_path : ∀ A₀ a b, A ≠ .path A₀ a b)
|
||
(h_not_glue : ∀ φG T f fInv sec ret coh Ai,
|
||
A ≠ .glue φG T f fInv sec ret coh Ai) :
|
||
eval env (.transp i A φ t) =
|
||
.vneu (.ntransp i A φ (eval env t)) := by
|
||
rw [eval_transp_nonpath env i A φ t hφ hA h_not_path h_not_glue]
|
||
exact vTransp_stuck _ _ _ _ hφ hA h_not_pi
|
||
|
||
/-- **T5 at eval level**: face-formula congruence for transport. When
|
||
two face formulas are semantically equal — i.e. `φ.eval env =
|
||
ψ.eval env` for every env — transport under them computes the same
|
||
result.
|
||
|
||
This is a claim about Rust's evaluator: it inspects face formulas
|
||
only through their truth-value semantics, not their syntactic form.
|
||
Two faces that classify the same set of environments produce the
|
||
same transport reduction, regardless of their concrete tree
|
||
structure (e.g. `(i=0) ∧ ⊤` vs `(i=0)`).
|
||
|
||
Stated at the eval level (rather than augmenting the partial def
|
||
with a face-normalisation prepass) so the existing eval-axiom set is
|
||
not re-audited. The Rust backend's discharge: a face-normalisation
|
||
routine ensures syntactically distinct but semantically equal faces
|
||
take the same dispatch branch. -/
|
||
axiom eval_transp_face_congr (env : CEnv) (i : DimVar) (A : CType)
|
||
(φ ψ : FaceFormula) (t : CTerm)
|
||
(h : ∀ ε, φ.eval ε = ψ.eval ε) :
|
||
eval env (.transp i A φ t) = eval env (.transp i A ψ t)
|
||
|
||
/-!
|
||
### `eval` on `.comp` — four disjoint cases
|
||
|
||
The old coarse `eval_comp` (which asserted the stuck form unconditionally)
|
||
is replaced by four case-axioms mirroring the arms of `vCompAtTerm`. The
|
||
cases are disjoint by precondition, so the axiom set is consistent.
|
||
-/
|
||
|
||
/-- **C1 at eval level**: with a full face, heterogeneous composition
|
||
reduces to the system body substituted at `i := 1`. Crucially this
|
||
is *term-level* substitution, not `vPApp` on the evaluated body —
|
||
`u` may be e.g. a free variable whose value doesn't look like a
|
||
function. -/
|
||
axiom eval_comp_top (env : CEnv) (i : DimVar) (A : CType) (u t : CTerm) :
|
||
eval env (.comp i A .top u t) = eval env (u.substDim i .one)
|
||
|
||
/-- **C2 at eval level**: with an empty face, the system contributes
|
||
nothing and composition reduces to plain transport. -/
|
||
axiom eval_comp_bot (env : CEnv) (i : DimVar) (A : CType) (u t : CTerm) :
|
||
eval env (.comp i A .bot u t) = eval env (.transp i A .bot t)
|
||
|
||
/-- **Constant-line comp = hcomp**: when the type `A` doesn't vary along
|
||
`i`, heterogeneous composition reduces to homogeneous composition on
|
||
the (fixed) type `A`. The tube is `.plam i u` — the system body `u`
|
||
packaged as a dim-closure over the line binder. -/
|
||
axiom eval_comp_const (env : CEnv) (i : DimVar) (A : CType)
|
||
(φ : FaceFormula) (u t : CTerm)
|
||
(hφ₁ : φ ≠ .top) (hφ₂ : φ ≠ .bot)
|
||
(hA : CType.dimAbsent i A = true) :
|
||
eval env (.comp i A φ u t) =
|
||
vHCompValue A φ (eval env (.plam i u)) (eval env t)
|
||
|
||
/-- **Heterogeneous Π comp**: when A is a pi type with a genuinely-varying
|
||
line, `vCompAtTerm` packages the comp into a `vCompFun` closure that
|
||
will run the CCHM β-rule when the function is applied. -/
|
||
axiom eval_comp_pi (env : CEnv) (i : DimVar) (domA codA : CType)
|
||
(φ : FaceFormula) (u t : CTerm)
|
||
(hφ₁ : φ ≠ .top) (hφ₂ : φ ≠ .bot)
|
||
(hA : CType.dimAbsent i (.pi domA codA) = false) :
|
||
eval env (.comp i (.pi domA codA) φ u t) =
|
||
.vCompFun env i domA codA φ u t
|
||
|
||
/-- Stuck fallback: `.comp` whose face is neither `.top` nor `.bot`, whose
|
||
line genuinely varies, and whose type is neither `.pi` nor a constant
|
||
produces a neutral. -/
|
||
axiom eval_comp_stuck (env : CEnv) (i : DimVar) (A : CType)
|
||
(φ : FaceFormula) (u t : CTerm)
|
||
(hφ₁ : φ ≠ .top) (hφ₂ : φ ≠ .bot)
|
||
(hA : CType.dimAbsent i A = false)
|
||
(h_not_pi : ∀ domA codA, A ≠ .pi domA codA) :
|
||
eval env (.comp i A φ u t) =
|
||
.vneu (.ncomp i A φ (eval env u) (eval env t))
|
||
|
||
/-- `eval` on `.compN` delegates to `vCompNAtTerm`. -/
|
||
axiom eval_compN (env : CEnv) (i : DimVar) (A : CType)
|
||
(clauses : List (FaceFormula × CTerm)) (t : CTerm) :
|
||
eval env (.compN i A clauses t) = vCompNAtTerm env i A clauses t
|
||
|
||
/-!
|
||
### `vHCompValue` — three disjoint cases
|
||
-/
|
||
|
||
/-- Homogeneous composition under a full face: the tube covers everything,
|
||
and the result is the tube evaluated at `1`. -/
|
||
axiom vHCompValue_top (A : CType) (tube base : CVal) :
|
||
vHCompValue A .top tube base = vPApp tube .one
|
||
|
||
/-- **CCHM Π hcomp rule**: homogeneous composition on a Π type produces
|
||
a `vHCompFun` closure that applies pointwise when its function is
|
||
applied to an argument. `domA` is stored in the type but unused in
|
||
the resulting closure because hcomp on the domain is trivial (A is
|
||
fixed, not varying). -/
|
||
axiom vHCompValue_pi (domA codA : CType) (φ : FaceFormula) (tube base : CVal)
|
||
(hφ : φ ≠ .top) :
|
||
vHCompValue (.pi domA codA) φ tube base = .vHCompFun codA φ tube base
|
||
|
||
/-- Stuck fallback: hcomp on a non-pi type under a non-top face. Uses
|
||
`nhcomp` (separate from `ncomp` because hcomp's type is fixed). -/
|
||
axiom vHCompValue_stuck (A : CType) (φ : FaceFormula) (tube base : CVal)
|
||
(hφ : φ ≠ .top)
|
||
(h_not_pi : ∀ domA codA, A ≠ .pi domA codA) :
|
||
vHCompValue A φ tube base = .vneu (.nhcomp A φ tube base)
|
||
|
||
-- Reduction lemmas for `vApp`.
|
||
|
||
axiom vApp_vlam (env : CEnv) (x : String) (body : CTerm) (arg : CVal) :
|
||
vApp (.vlam env x body) arg = eval (env.extend x arg) body
|
||
|
||
axiom vApp_vneu (n : CNeu) (arg : CVal) :
|
||
vApp (.vneu n) arg = .vneu (.napp n arg)
|
||
|
||
/-- Full CCHM Π β-rule at the value level: applying a transported-function
|
||
closure to an argument `arg` inversely transports `arg` through the
|
||
domain, applies the underlying function `f`, and forward-transports the
|
||
result through the codomain.
|
||
|
||
When `CType.dimAbsent i domA = true`, `vTranspInv` reduces to identity
|
||
(by `vTranspInv_const`) and this specialises to the simpler
|
||
const-domain rule `vTransp i codA φ (vApp f arg)`. -/
|
||
axiom vApp_vTranspFun (i : DimVar) (domA codA : CType) (φ : FaceFormula)
|
||
(f : CVal) (arg : CVal) :
|
||
vApp (.vTranspFun i domA codA φ f) arg =
|
||
vTransp i codA φ (vApp f (vTranspInv i domA φ arg))
|
||
|
||
/-- **CCHM Π hcomp β-rule** at the value level: applying a homogeneously
|
||
composed function closure to `arg` yields hcomp on the codomain with:
|
||
· tube = `λj. (tube @ j) arg` (represented by `vTubeApp`),
|
||
· base = `base arg`.
|
||
No inverse transport — hcomp's type is fixed, so the argument passes
|
||
through unchanged. -/
|
||
axiom vApp_vHCompFun (codA : CType) (φ : FaceFormula) (tube base arg : CVal) :
|
||
vApp (.vHCompFun codA φ tube base) arg =
|
||
vHCompValue codA φ (.vTubeApp tube arg) (vApp base arg)
|
||
|
||
/-- **Full CCHM Π hetero comp β-rule**: applying `comp^i (pi A B) φ u u₀` to
|
||
`y : A(1)` unfolds via the *fill* construction. For a fresh dim `$fj`
|
||
and argument name `$y`:
|
||
· `y_at_i = transp^{$fj} (A[i := inv $fj ∨ i]) φ y` — fill from A(1) down to A(i).
|
||
· `y_at_0 = transp^{$fj} (A[i := inv $fj]) φ y` — reversed-line transport A(1) → A(0).
|
||
The result is `comp^i B(i) φ (u (y_at_i)) (u₀ (y_at_0))` evaluated in
|
||
an env extending `$y` with the argument.
|
||
|
||
When `CType.dimAbsent i domA = true`, both fills degenerate to `y`
|
||
(by T2 on the constant line), recovering the simpler const-domain
|
||
formula `comp^i B(i) φ (u y) (u₀ y)`.
|
||
|
||
Hygiene assumption: `$y` is not a user variable in `env`, and `$fj`
|
||
is not a user DimVar in `domA`, `codA`, `φ`, `u`, `t`. These reserved
|
||
names are chosen to minimise collision probability. -/
|
||
axiom vApp_vCompFun (env : CEnv) (i : DimVar) (domA codA : CType)
|
||
(φ : FaceFormula) (u t : CTerm) (arg : CVal) :
|
||
vApp (.vCompFun env i domA codA φ u t) arg =
|
||
eval (env.extend "$y" arg) (.comp i codA φ
|
||
(.app u (.transp ⟨"$fj"⟩
|
||
(domA.substDimExpr i (.join (.inv (.var ⟨"$fj"⟩)) (.var i)))
|
||
φ (.var "$y")))
|
||
(.app t (.transp ⟨"$fj"⟩
|
||
(domA.substDimExpr i (.inv (.var ⟨"$fj"⟩)))
|
||
φ (.var "$y"))))
|
||
|
||
-- Reduction lemmas for `vPApp`.
|
||
|
||
axiom vPApp_vplam (env : CEnv) (i : DimVar) (body : CTerm) (r : DimExpr) :
|
||
vPApp (.vplam env i body) r = eval env (body.substDim i r)
|
||
|
||
axiom vPApp_vneu (n : CNeu) (r : DimExpr) :
|
||
vPApp (.vneu n) r = .vneu (.npapp n r)
|
||
|
||
/-- `vTubeApp tube arg` under dim application reduces to `(tube @ r) arg`.
|
||
Encodes the semantic meaning of `λj. (tube @ j) arg`. -/
|
||
axiom vPApp_vTubeApp (tube arg : CVal) (r : DimExpr) :
|
||
vPApp (.vTubeApp tube arg) r = vApp (vPApp tube r) arg
|
||
|
||
/-!
|
||
### `vCompNAtTerm` — compound equation mirroring the partial-def arms
|
||
|
||
Single axiom exposing the full case analysis so that derived theorems can
|
||
pattern-match on the clause list's structure. -/
|
||
|
||
axiom vCompNAtTerm_def (env : CEnv) (i : DimVar) (A : CType)
|
||
(clauses : List (FaceFormula × CTerm)) (t : CTerm) :
|
||
vCompNAtTerm env i A clauses t =
|
||
match clauses.find?
|
||
(fun ⟨φ, _⟩ => match φ with | .top => true | _ => false) with
|
||
| some ⟨_, u⟩ => eval env (u.substDim i .one)
|
||
| none =>
|
||
let live := clauses.filter
|
||
(fun ⟨φ, _⟩ => match φ with | .bot => false | _ => true)
|
||
match live with
|
||
| [] => eval env (.transp i A .bot t)
|
||
| [⟨φ, u⟩] => vCompAtTerm env i A φ u t
|
||
| _ => .vneu (.ncompN env i A
|
||
(live.map (fun ⟨φ, u⟩ => (φ, eval env u)))
|
||
(eval env t))
|
||
|
||
/-!
|
||
### Path transport endpoint reductions
|
||
|
||
Applied at `.zero`, the CCHM `(j=0)` clause fires, giving `a(1)`.
|
||
Applied at `.one`, the `(j=1)` clause fires, giving `b(1)`.
|
||
Applied at any other DimExpr, the multi-clause comp can't be computed
|
||
without machinery we haven't built yet — stalls at `npathTranspApp`.
|
||
The three axioms are disjoint by the shape of the DimExpr argument.
|
||
-/
|
||
|
||
/-- Path transport at left endpoint: result is `a(1)`, i.e. `a` evaluated
|
||
with `i` substituted by `.one`. This is *not* a transport of `a(0)` —
|
||
it's the line's specified endpoint at `i=1`, made forced by CCHM's
|
||
multi-clause `(j=0)` constraint. -/
|
||
axiom vPApp_vPathTransp_zero
|
||
(env : CEnv) (i : DimVar) (A : CType) (a b : CTerm) (φ : FaceFormula)
|
||
(p : CTerm) :
|
||
vPApp (.vPathTransp env i A a b φ p) .zero =
|
||
eval env (a.substDim i .one)
|
||
|
||
/-- Path transport at right endpoint: result is `b(1)`. -/
|
||
axiom vPApp_vPathTransp_one
|
||
(env : CEnv) (i : DimVar) (A : CType) (a b : CTerm) (φ : FaceFormula)
|
||
(p : CTerm) :
|
||
vPApp (.vPathTransp env i A a b φ p) .one =
|
||
eval env (b.substDim i .one)
|
||
|
||
/-- **Full CCHM path transport at a generic dim**: apply the path
|
||
transport at `r` by evaluating the CCHM multi-clause comp
|
||
`comp^i A [φ ↦ p@r, (r=0) ↦ a, (r=1) ↦ b] (p@r)` via `vCompNAtTerm`.
|
||
This genuinely unsticks when `r`'s face substitution simplifies:
|
||
· `r = .zero` → `(r=0)` becomes `.top`, firing the left clause → `a(1)`.
|
||
· `r = .one` → `(r=1)` becomes `.top`, firing the right clause → `b(1)`.
|
||
· `r = .var k` generic → both clauses are non-trivial; stalls at a
|
||
structured `ncompN` neutral that can still unstick if `k` later
|
||
becomes an endpoint. -/
|
||
axiom vPApp_vPathTransp_general
|
||
(env : CEnv) (i : DimVar) (A : CType) (a b : CTerm) (φ : FaceFormula)
|
||
(p : CTerm) (r : DimExpr)
|
||
(h_zero : r ≠ .zero) (h_one : r ≠ .one) :
|
||
vPApp (.vPathTransp env i A a b φ p) r =
|
||
vCompNAtTerm env i A
|
||
[ (φ, .papp p r)
|
||
, (FaceFormula.dimExprEq0 r, a)
|
||
, (FaceFormula.dimExprEq1 r, b) ]
|
||
(.papp p r)
|
||
|
||
/-!
|
||
### `eval` on `.glueIn` — three disjoint cases
|
||
|
||
`glueIn [φ ↦ t] a` evaluates differently on/off/between the face `φ`:
|
||
· `φ = .top`: the face is full, the result is `t` (only the T-side
|
||
matters).
|
||
· `φ = .bot`: the face is empty, the result is `a` (only the A-side
|
||
matters).
|
||
· Otherwise: a structurally stuck `nglueIn` neutral preserving both
|
||
face-on and face-off values — later dim substitution into `φ` may
|
||
collapse it.
|
||
|
||
The three cases are mutually exclusive by precondition. -/
|
||
|
||
/-- (1) Full-face glueIn reduces to the T-side. -/
|
||
axiom eval_glueIn_top (env : CEnv) (t a : CTerm) :
|
||
eval env (.glueIn .top t a) = eval env t
|
||
|
||
/-- (2) Empty-face glueIn reduces to the A-side. -/
|
||
axiom eval_glueIn_bot (env : CEnv) (t a : CTerm) :
|
||
eval env (.glueIn .bot t a) = eval env a
|
||
|
||
/-- (3) Neutral-face glueIn produces an `nglueIn` stuck neutral preserving
|
||
both evaluated sides. The face formula is kept syntactic so that
|
||
later dim substitution can resolve it to `.top` or `.bot`.
|
||
|
||
The `h_not_unglue` precondition rules out the η-redex shape
|
||
`.glueIn φ t (.unglue φ f g)`, which reduces via
|
||
`eval_glueIn_of_unglue` to `eval env g` instead of a stuck form.
|
||
Without this restriction, the stuck rule and the η-rule would
|
||
disagree on a common instance. -/
|
||
axiom eval_glueIn_stuck (env : CEnv) (φ : FaceFormula) (t a : CTerm)
|
||
(hφ₁ : φ ≠ .top) (hφ₂ : φ ≠ .bot)
|
||
(h_not_unglue : ∀ f g, a ≠ .unglue φ f g) :
|
||
eval env (.glueIn φ t a) = .vneu (.nglueIn φ (eval env t) (eval env a))
|
||
|
||
/-!
|
||
### `eval` on `.unglue` — three disjoint cases
|
||
|
||
`unglue [φ ↦ f] g` — extracting an A-value from a glued term:
|
||
· `φ = .top`: apply the forward map: `vApp (eval f) (eval g)`.
|
||
· `φ = .bot`: identity. `glueIn .bot t a` reduces to `a`, so the
|
||
argument is already an A-value; unglue is the identity there.
|
||
· Otherwise: stuck `nunglue` preserving `f` and `g`.
|
||
|
||
All three cases are mutually exclusive. -/
|
||
|
||
/-- (1) Full-face unglue: apply the forward map pointwise. -/
|
||
axiom eval_unglue_top (env : CEnv) (f g : CTerm) :
|
||
eval env (.unglue .top f g) = vApp (eval env f) (eval env g)
|
||
|
||
/-- (2) Empty-face unglue: identity on `g`. This is the definitional
|
||
content of `Glue [bot ↦ (T, e)] A = A`: values are already A-values. -/
|
||
axiom eval_unglue_bot (env : CEnv) (f g : CTerm) :
|
||
eval env (.unglue .bot f g) = eval env g
|
||
|
||
/-- (3) Neutral-face unglue: produce a stuck `nunglue` neutral preserving
|
||
`f` and `g`. Later dim substitution into `φ` may resolve it to
|
||
`.top` (→ forward-map application) or `.bot` (→ identity).
|
||
|
||
The `h_not_glueIn` precondition rules out the β-redex shape
|
||
`.unglue φ f (.glueIn φ t a)`, which reduces via
|
||
`eval_unglue_of_glueIn` to `eval env a` under the overlap
|
||
condition. Without this restriction, the stuck rule and the
|
||
β-rule would disagree on a common instance. -/
|
||
axiom eval_unglue_stuck (env : CEnv) (φ : FaceFormula) (f g : CTerm)
|
||
(hφ₁ : φ ≠ .top) (hφ₂ : φ ≠ .bot)
|
||
(h_not_glueIn : ∀ t a, g ≠ .glueIn φ t a) :
|
||
eval env (.unglue φ f g) = .vneu (.nunglue φ (eval env f) (eval env g))
|
||
|
||
/-!
|
||
### Glue β- and η-rules (eval level)
|
||
|
||
The β-rule (`unglue · glueIn`) and η-rule (`glueIn · unglue`) on
|
||
matching face formulas fire *regardless* of whether `φ` is `.top`,
|
||
`.bot`, or stuck. On `.top` / `.bot` the existing face-dispatched
|
||
axioms reduce to the same result under the overlap condition
|
||
(consistency check below); on stuck faces, these rules are the only
|
||
way the redex reduces — `eval_unglue_stuck` and `eval_glueIn_stuck`
|
||
carry `h_not_glueIn` / `h_not_unglue` preconditions to avoid conflict.
|
||
|
||
The `h_overlap` condition encodes the CCHM typing-side constraint
|
||
`e.f t = a` (or `t = e.f g`) that accompanies a well-typed glueIn /
|
||
unglue — the evaluator assumes it and short-circuits.
|
||
-/
|
||
|
||
/-- β-rule: `unglue φ f (glueIn φ t a)` reduces to `a` under the
|
||
overlap condition. Rust-discharge: the evaluator recognises the
|
||
nested pattern and short-circuits when the overlap invariant holds
|
||
(typing guarantees it). -/
|
||
axiom eval_unglue_of_glueIn (env : CEnv) (φ : FaceFormula) (f t a : CTerm)
|
||
(h_overlap : eval env (.app f t) = eval env a) :
|
||
eval env (.unglue φ f (.glueIn φ t a)) = eval env a
|
||
|
||
/-- η-rule: `glueIn φ t (unglue φ f g)` reduces to `g` under the
|
||
overlap condition. Rust-discharge: dual to `eval_unglue_of_glueIn`. -/
|
||
axiom eval_glueIn_of_unglue (env : CEnv) (φ : FaceFormula) (f t g : CTerm)
|
||
(h_overlap : eval env t = eval env (.app f g)) :
|
||
eval env (.glueIn φ t (.unglue φ f g)) = eval env g
|
||
|
||
/-!
|
||
### `eval` on Σ constructors — three arms
|
||
|
||
`.pair a b` evaluates component-wise to `.vpair`; `.fst t` / `.snd t`
|
||
project through `vFst` / `vSnd`, which themselves β-reduce on `vpair`
|
||
and produce stuck `.nfst` / `.nsnd` on neutrals.
|
||
-/
|
||
|
||
/-- Pair construction evaluates component-wise. -/
|
||
axiom eval_pair (env : CEnv) (a b : CTerm) :
|
||
eval env (.pair a b) = .vpair (eval env a) (eval env b)
|
||
|
||
/-- First projection delegates to `vFst`. -/
|
||
axiom eval_fst (env : CEnv) (t : CTerm) :
|
||
eval env (.fst t) = vFst (eval env t)
|
||
|
||
/-- Second projection delegates to `vSnd`. -/
|
||
axiom eval_snd (env : CEnv) (t : CTerm) :
|
||
eval env (.snd t) = vSnd (eval env t)
|
||
|
||
/-- β-rule for `vFst` on a pair. -/
|
||
axiom vFst_vpair (a b : CVal) : vFst (.vpair a b) = a
|
||
|
||
/-- β-rule for `vSnd` on a pair. -/
|
||
axiom vSnd_vpair (a b : CVal) : vSnd (.vpair a b) = b
|
||
|
||
/-- `vFst` on a neutral produces a stuck `nfst` neutral. -/
|
||
axiom vFst_vneu (n : CNeu) : vFst (.vneu n) = .vneu (.nfst n)
|
||
|
||
/-- `vSnd` on a neutral produces a stuck `nsnd` neutral. -/
|
||
axiom vSnd_vneu (n : CNeu) : vSnd (.vneu n) = .vneu (.nsnd n)
|