Background
The typechecker currently performs NbE evaluation only for type-level computation (comparing types in val_eq, instantiating dependent Pi closures). It does not normalize arithmetic expressions in terms — those remain as App(Prim::Add, ...) nodes in the core IR and are only reduced by the staging evaluator.
This is fine for the current prototype, but will become load-bearing once dependent types are in use. Consider a type like:
The typechecker needs to be able to normalize n + 1 (and reason about its relationship to other size expressions) in order to typecheck programs that use Vec.
The wrapping semantics question
The meta-evaluator now uses wrapping arithmetic (matching Wasm's i32/i64) for phase coherence between fn and code fn. However, wrapping semantics are a poor fit for size indices: Vec(u32, 255 + 1) silently producing a 0-element vector would be surprising and unsafe.
This suggests that size indices may want a separate integer type — perhaps an unbounded Nat, or a large fixed-width type treated as a proof-irrelevant index — with distinct (non-wrapping, or checked) arithmetic semantics. This is a common pattern in dependently-typed languages (Agda's ℕ, Idris's Nat).
Open questions
- Should size indices use a dedicated
Nat-like type, or a fixed-width integer with non-wrapping semantics?
- If a separate type, how does it relate to object-level integer types? (Lifting, coercions, bounded proofs?)
- How does normalization interact with the existing NbE evaluator — does size arithmetic share the same evaluator, or get a separate reduction strategy?
- What normal forms are needed for the typechecker to decide type equality of expressions like
Vec(A, m + n) vs Vec(A, n + m)?
Relationship to phase polymorphism
With phase polymorphism, a function quantifying over a size n : ??? and returning Vec(A, n) needs the size type to be well-behaved at both meta and object phase. This further constrains the design.
Background
The typechecker currently performs NbE evaluation only for type-level computation (comparing types in
val_eq, instantiating dependent Pi closures). It does not normalize arithmetic expressions in terms — those remain asApp(Prim::Add, ...)nodes in the core IR and are only reduced by the staging evaluator.This is fine for the current prototype, but will become load-bearing once dependent types are in use. Consider a type like:
The typechecker needs to be able to normalize
n + 1(and reason about its relationship to other size expressions) in order to typecheck programs that useVec.The wrapping semantics question
The meta-evaluator now uses wrapping arithmetic (matching Wasm's
i32/i64) for phase coherence betweenfnandcode fn. However, wrapping semantics are a poor fit for size indices:Vec(u32, 255 + 1)silently producing a 0-element vector would be surprising and unsafe.This suggests that size indices may want a separate integer type — perhaps an unbounded
Nat, or a large fixed-width type treated as a proof-irrelevant index — with distinct (non-wrapping, or checked) arithmetic semantics. This is a common pattern in dependently-typed languages (Agda'sℕ, Idris'sNat).Open questions
Nat-like type, or a fixed-width integer with non-wrapping semantics?Vec(A, m + n)vsVec(A, n + m)?Relationship to phase polymorphism
With phase polymorphism, a function quantifying over a size
n : ???and returningVec(A, n)needs the size type to be well-behaved at both meta and object phase. This further constrains the design.