Files
rose-ash/plans/idris-on-sx.md
giles 9dd9fb9c37 plans: layered-stack framing + chisel sequence + loop scaffolding
Design + ops scaffolding for the next phase of work, none of it touching
substrate or guest code.

lib-guest.md: rewrites Architectural framing as a 5-layer stack
  (substrate → lib/guest → languages → shared/ → applications),
  recursive dependency-direction rule, scaled two-consumer rule. Adds
  Phase B (long-running stratification) with sub-layer matrix
  (core/typed/relational/effects/layout/lazy/oo), language profiles, and
  the long-running-discipline section. Preserves existing Phase A
  progress log and rules.

ocaml-on-sx.md: scope reduced to substrate validation + HM + reference
  oracle. Phases 1-5 + minimal stdlib slice + vendored testsuite slice.
  Dream carved out into dream-on-sx.md; Phase 8 (ReasonML) deferred.
  Records lib-guest sequencing dependency.

datalog-on-sx.md: adds Phase 4 built-in predicates + body arithmetic,
  Phase 6 magic sets, safety analysis in Phase 3, Non-goals section.

New chisel plans (forward-looking, not yet launchable):
  kernel-on-sx.md       — first-class everything, env-as-value endgame
  idris-on-sx.md        — dependent types, evidence chisel
  probabilistic-on-sx.md — weighted nondeterminism + traces
  maude-on-sx.md        — rewriting as primitive
  linear-on-sx.md       — resource model, artdag-relevant

Loop briefings (4 active, 1 cold):
  minikanren-loop.md, ocaml-loop.md, datalog-loop.md, elm-loop.md, koka-loop.md

Restore scripts mirror the loop pattern:
  restore-{minikanren,ocaml,datalog,jit-perf,lib-guest}.sh
  Each captures worktree state, plan progress, MCP health, tmux status.
  Includes the .mcp.json absolute-path patch instruction (fresh worktrees
  have no _build/, so the relative mcp_tree path fails on first launch).

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

7.7 KiB

Idris-on-SX: dependent types as substrate stress test

The single most substrate-stretching language in the set. Dependent types unify the term and type universes — types may depend on values, normalisation becomes part of type-checking, decidable equality matters, totality has to be checked. Idris 2 is the pragmatic choice: smaller than Agda, more accessible than Coq, designed for general programming rather than proof-only.

The chisel: evidence. Currently every typed guest in the set (OCaml, Haskell, Elm, Koka, Reasonml) lives in HM-or-rank-1 territory — types are simple-enough algebra. Dependent types force the substrate to think about terms as evidence: what does it mean for a value to witness a type? what's a normal form? when are two terms equal up to computation?

What this exposes about the substrate:

  • Whether SX values can carry typing evidence efficiently, or whether a separate elaboration phase is required.
  • Whether normalisation (beta, iota, delta) is fast enough at type-check time — implicates JIT, allocation, and frame management.
  • Whether decidable equality of arbitrary values is reachable.
  • Whether erasure (proofs deleted at runtime) can be expressed cleanly given SX's value model.
  • Whether HM (lib/guest/typed/hm.sx) extends cleanly to bidirectional dependent inference, or whether they're genuinely different machinery.

End-state goal: core Idris 2 — Pi types, indexed families, dependent pattern matching, totality checking, erasure, holes for interactive development. Not the full Idris 2 stdlib; a faithful core that runs idiomatic dependent programs.

Ground rules

  • Scope: lib/idris/** and plans/idris-on-sx.md only. Substrate gaps → sx-improvements.md, do not fix from this plan.
  • Consumes from lib/guest/: core/lex, core/pratt (Idris has indentation but Pratt for ops), core/match, layout/ (Idris is whitespace-sensitive), typed/hm.sx (as a starting point that gets extended).
  • Will propose a new sub-layer lib/guest/dependent/ — universes, conversion checking, normalisation, bidirectional elaboration. A second consumer is genuinely speculative for now; accept "second user TBD" until a Lean-fragment or Agda-fragment plan emerges.
  • Branch: loops/idris. Standard worktree pattern.

Architecture sketch

Idris source text
    │
    ▼
lib/idris/parser.sx        — Haskell-ish, layout-sensitive, type-level syntax
    │                        (consumes lib/guest/layout, lib/guest/pratt)
    ▼
lib/idris/elaborate.sx     — surface → core: implicit args, holes, do-notation
    │
    ▼
lib/idris/check.sx         — bidirectional dependent type-checker
    │                        infer / check modes, conversion via normalisation
    ▼
lib/idris/normalise.sx     — NbE (normalisation by evaluation): values are
    │                        semantic, neutral terms hold reflected applications
    ▼
lib/idris/runtime.sx       — erased terms run via standard SX evaluation;
                             constructors as tagged ADTs from sx-improvements

Semantic mappings

Idris construct SX mapping
(x : Nat) -> P x dependent function type — first-class {:type :pi :name x :domain Nat :codomain (P x)}
\x => body (fn (x) body) — same as before
data Vect : Nat -> Type -> Type indexed family — define-type extension carrying index
Vect (S n) a applied type former — neutral term until index is ground
case x of pat => e dependent pattern match — refines indices in branches
(x : A) ** B x dependent pair — {:type :sigma :name x :first A :second (B x)}
?hole unfilled term — type-checker reports goal type
Refl : x = x propositional equality witness
total totality check — termination + coverage

Roadmap

Phase 1 — Parser + layout

  • Lexer/parser via lib/guest/lex + lib/guest/pratt.
  • Layout via lib/guest/layout — Idris uses indentation similar to Haskell.
  • Type signatures name : Type, function definitions with multiple clauses.
  • Tests in lib/idris/tests/parse.sx.

Phase 2 — Untyped evaluator (sanity check)

  • Strip types entirely; run programs as untyped lambda calculus + ADTs.
  • Goal: factorial, list ops, recursive datatypes work without type-checking.
  • Confirms the runtime story before tackling the type checker.

Phase 3 — Bidirectional simply-typed checking + universes

  • Hierarchy of universes Type 0 : Type 1 : Type 2 : ....
  • Check mode (push expected type), infer mode (synthesise type).
  • Variable / lambda / application / annotation rules.
  • Tests: simple programs that succeed/fail type-check.

Phase 4 — Pi types + dependent functions

  • Pi as a first-class type former.
  • Application instantiates the codomain at the argument value.
  • Conversion check: are two types equal up to normalisation?
  • Implement NbE — values are either canonical (constructors, functions) or neutral (stuck applications); conversion compares via reify.
  • Tests: id : (a : Type) -> a -> a, const, flip.

Phase 5 — Indexed families + dependent pattern matching

  • data Vect : Nat -> Type -> Type — constructors carry index.
  • Pattern match refines indices in branches (Cons case has n = S k).
  • Coverage check (incomplete matches reported).
  • Tests: head : Vect (S n) a -> a (rejects empty vectors at compile time).

Phase 6 — Totality / termination

  • Termination checker: structural recursion, sized types or call graphs.
  • Productivity for codata.
  • total / partial annotations.
  • Tests: recursive programs that pass / fail termination.

Phase 7 — Erasure

  • Mark proof-only arguments as erased.
  • Runtime evaluation skips erased subterms.
  • Tests: vector head runs at the speed of list head (proof argument deleted).

Phase 8 — Holes + interactive development

  • ?name produces a hole with reported goal type.
  • Tactic-like elaboration step (small set: intro, apply, case-split).
  • Tests: develop a program by progressive hole-filling.

Phase 9 — Propose lib/guest/dependent/

  • Identify reusable universe machinery, conversion-checking framework, NbE infrastructure.
  • Hold off on extraction until a second consumer (Lean-fragment, Agda-fragment) is plausible.

lib/guest feedback loop

Consumes: core/lex, core/pratt, core/match, layout/, typed/hm.sx (as starting point).

Stresses substrate: value normalisation cost (every type-check step normalises); decidable equality across closures; whether ADT primitive (define-type from sx-improvements Phase 3) handles indexed families.

May propose: lib/guest/dependent/ sub-layer — universes, NbE, conversion checking, bidirectional elaboration. Speculative second consumer until Lean/Agda-fragment plans materialise.

What it teaches: whether SX's substrate scales to type-level computation. Most languages have a clean separation: types are static, terms are dynamic. Idris collapses them. If SX can host this in <5000 lines, the substrate is genuinely paradigm-agnostic. If it can't, "paradigm-agnostic" was overclaiming.

References

  • Brady, "Type-Driven Development with Idris" (Manning, 2017).
  • Idris 2 source: https://github.com/idris-lang/Idris2
  • Coquand & Dybjer "An Algorithm for Type-Checking Dependent Types" (NbE foundations).
  • Christiansen, "Functional Programming in Lean" (cleanest exposition of bidirectional dependent checking).

Progress log

(awaiting completion of Kernel-on-SX or substrate ADT primitive maturity, whichever happens first)

Blockers

(speculative — main risk is substrate normalisation cost)