rune

module
v3.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 13, 2026 License: MIT

README

Rune

goforge.dev/rune/v3 — the kernel of a small, content-addressed, dependently typed language. A finished proof is a value, and its identity is the hash of its content, so verification becomes a cache that never recomputes. Dependent types are a build-time discipline; what deploys is the erasure — the shadow. The parts people fight type-theory wars over — the codegen target, the resource semiring, the notion of equality — each sit behind a clean interface so the right one lives in the right layer.

v3.0.0

v3 puts a second equality behind the interface the first one defined — two-level type theory, shipped the way v2 shipped quotients: a builtin content-addressed group, zero new core syntax, zero hash-format change. The strict, proof-irrelevant Eq (UIP and all) stays exactly as it was and keeps doing the metatheoretic work; alongside it, a fibrant universe à la Tarski houses the inner layer:

  • UF, El, fib, piF — fibrant types as codes, with decoding that COMPUTES: El (fib A) is A, El (piF A B) is the genuine function type, so inner functions are plain lambdas.
  • Inner identity, twice. Point-level pathF/preflF/pathJ — J with its β-rule on refl (the v1 equality chapter replays one level in). And type-level pathU/ureflU/ua — univalence, postulated: the inhabitant UIP forbids globally now lives locally, as a permanently neutral head the strict layer can quantify over but never collapse. pathU A B is data, not Prop — UIP never applies to inner paths, so the two equalities never name the same relation and never contradict.
  • Transport computes. castU reduces on ureflU AND through ua: castU … (ua … not …) b IS not b, provable by bare refl. Transport across an equivalence as a programming idiom — literally. What does NOT compute — path induction over a ua-path, the higher coherences — is the labelled §F frontier (cubical inner layer), postulated, not sold.
  • Check, not run — honestly enforced. The v3 criterion is that the inner chapters elaborate and CHECK (listings/ch09ch10). Erasure has no meaning for ua-paths yet, so EmitProgram skips inner-tainted definitions and refuses a tainted main, rather than silently emitting the identity where the checker computed not.

The thesis, end to end: names where you read, quantities as a parameter, targets as a menu, and equality as a stratum — even the notion everyone said you'd have to leave home for. See ref_docs/rune-v3-implementation.md for why the feared two-layer machinery (fibrancy judgments, layer-tracking elaboration, a second cache seam) collapsed into ordinary typing.

v2.0.0

v2 extends the equality stratum with quotients — and proves the extension thesis: the core grew a new type former without one new core constructor, one new token of syntax, or a hash-format bump. The release criterion is the same shape as v1: the chapters that use a quotient or a set-level higher inductive (listings/ch06ch08) elaborate, check, and run, and the core gains nothing they don't use.

  • A / R as a first-class typeQuot A R, with point introduction qin, path introduction qsound : R a b -> Eq (Quot A R) (qin … a) (qin … b), the eliminator qlift (one respect proof, paid once, then forgotten), and Prop-motive induction qind (respect free, by proof irrelevance). The five are a builtin group of bodiless, content-addressed definitions — the Phase-4 datatype mechanism, reused whole — and qlift/qind compute by quotient ι-rules in the evaluator: qlift … (qin … a) IS f a.
  • Eq stays uniform. Equality at a quotient type is stuck; identification is introduced by qsound and consumed by the irrelevance machinery v1 already has. UIP holds untouched; quotient effectiveness is parked with its upgrade path (ref_docs/rune-v2-implementation.md).
  • Truncation costs nothing. ‖A‖ is the Church encoding in impredicative Prop (listings/ch08) — introduction, elimination into Prop, and functoriality as plain definitions. No former ships because no chapter needs one.
  • The shadow stays boring. A quotient compiles to its carrier: qin is the identity at runtime, a lift is a plain call, and every respect proof and path is a unit. rune run listings/ch07_integers.rune zresult computes 2 + (−1) on ℤ under node with the proofs verifiably absent.
  • The chapters: a parity quotient (ch06); ℤ as pairs-mod-equal-differences with zadd by double lift — where the outer respect obligation is an equality of functions, so funext-as-reduction hands it to a pointwise qind — plus znegInvol and zaddComm by quotient induction (ch07); truncation and the propositional toolkit (ch08).

v1.0.0

The release criterion was that every listing in listings/ (the book's code) elaborates, checks, and runs against this core — and the core contains nothing the listings do not use. harness/listings_test.go enforces it. What v1 ships:

  • The MLTT core with glued NbE — eval/quote over a glued value domain (neutrals carry an un-unfolded spine plus a lazy unfolding), βδηι-conversion with a fast syntactic path that forces only on mismatch.
  • The proof cache — checking is wrapped, not phased: forcing a glued neutral IS store.Unfold, the sole body gateway, and logs the dependency. Certificates are keyed (content-hash, ‖unfolded-set‖); the table is append-only and there is no invalidation logic anywhere (see ref_docs/rune-proof-cache-semantics.md; the Frame Lemma is a live property test).
  • Elaboration — bidirectional checking with contextual metavariables, Miller pattern unification, implicit arguments ({x : A}, f {e}) and holes (_).
  • Observational equality (Pujet–Tabareau) — proof-irrelevant Prop, Eq computing on type structure (funext is a reduction), cast and subst computing on types and never inspecting proofs; UIP holds canonically.
  • Data by eliminatorsdata … is … end declares a content-addressed group (former, constructors, generated eliminator) with strict positivity; the eliminator is the only recursion principle, so coverage and totality are by construction and proofs by induction compute.
  • QTT — binders carry 0/1/ω quantities; the elaborator counts usage; the 0-fragment is the erasure boundary.
  • The predicative universe hierarchyU (= U_0), U1…; U_i : U_{i+1}, cumulativity, impredicative Prop at the bottom. No type-in-type.
  • Codegen — erasure to a target-independent IR and a JavaScript backend: rune emit FILE [NAME], rune run FILE NAME (needs node). Proofs, types, and the 0-fragment are verifiably absent from the output.

Three representations, one rule: the surface is named; the core is locally nameless (de Bruijn indices; top-level references are content hashes); the pretty-printer is the way back. Hashing is structural over elaborated core — never modulo conversion — so alpha-equivalent definitions are literally equal.

Build, test, run

go install goforge.dev/rune/v3/cmd/rune@latest

go test ./...        # the whole gate, including the listings corpus
rune fmt  FILE       # parse -> elaborate -> check -> pretty-print
rune hash FILE       # per definition: the Merkle hash of its checked core
rune repl            # type check + normalize, definitions cached; Nat
                     # arithmetic preloaded (--no-prelude for a bare session)
rune emit FILE NAME  # the erased JavaScript shadow, to stdout
rune run  FILE NAME  # emit and execute under node

Everything rune touches is TYPE CHECKED first; ill-typed files are rejected with the mismatch, not silently processed.

The listings

listings/ is the book-in-progress (Specify & Verify) as runnable code: functions and universes, implicits, observational equality (funext computes, sym/trans by transport), data and induction (a proof of add n zero = n that reduces to refl at numerals), quantities, and — as of v2 — quotients (a parity quotient, then ℤ with lifted arithmetic and quotient-induction theorems), propositional truncation, and — as of v3 — the two-level chapters: the fibrant layer and inner path induction, then postulated univalence with computing transport. The harness loads every chapter, checks every definition, normalizes the marked expressions, runs the data and quotient chapters through the JS backend, and asserts that inner-layer constructions refuse to deploy.

See CLAUDE.md for the architecture and standing rules, NON-GOALS.md for what this core deliberately is not, PARKING-LOT.md for what was deferred and why, and ref_docs/ for the v1–v3 design documents.

Directories

Path Synopsis
cmd
rune command
Command rune is the Phase-0 CLI for goforge.dev/rune.
Command rune is the Phase-0 CLI for goforge.dev/rune.
Package codegen is the CODEGEN STRATUM: erased intermediate representation to target source, kept behind the Backend interface so a new deployment target is one plugin, not a fork.
Package codegen is the CODEGEN STRATUM: erased intermediate representation to target source, kept behind the Backend interface so a new deployment target is one plugin, not a fork.
Package core holds Rune's locally-nameless core: the elaborated term language (Tm), the glued NbE value domain (Val, shape only in Phase 0), and structural Merkle hashing.
Package core holds Rune's locally-nameless core: the elaborated term language (Tm), the glued NbE value domain (Val, shape only in Phase 0), and structural Merkle hashing.
Package elaborate is the typed pipeline: bidirectional type checking.
Package elaborate is the typed pipeline: bidirectional type checking.
Package equality is the EQUALITY STRATUM: the notion of equality, kept behind a bounded interface so the roadmap can extend it (v2: quotients) and swap it (v3: two-level type theory) without a rewrite.
Package equality is the EQUALITY STRATUM: the notion of equality, kept behind a bounded interface so the roadmap can extend it (v2: quotients) and swap it (v3: two-level type theory) without a rewrite.
Package harness is the property-test scaffolding — the gate from day one.
Package harness is the property-test scaffolding — the gate from day one.
internal
repl
Package repl is the rune REPL: a read -> elaborate -> check -> normalize -> show loop over the shared session pipeline.
Package repl is the rune REPL: a read -> elaborate -> check -> normalize -> show loop over the shared session pipeline.
session
Package session is the shared parse -> resolve -> check -> hash pipeline that both the file commands (rune fmt / rune hash) and the REPL drive, so the pipeline lives in one place and is never duplicated.
Package session is the shared parse -> resolve -> check -> hash pipeline that both the file commands (rune fmt / rune hash) and the REPL drive, so the pipeline lives in one place and is never duplicated.
Package quantity is the QUANTITY STRATUM: the usage semiring that the quantitative type theory threads through binders (Phase 5), kept behind an interface so the lattice is a swappable module.
Package quantity is the QUANTITY STRATUM: the usage semiring that the quantitative type theory threads through binders (Phase 5), kept behind an interface so the lattice is a swappable module.
Package store holds the content-addressed definition store and is the home of the body-abstraction barrier.
Package store holds the content-addressed definition store and is the home of the body-abstraction barrier.
Package surface is Rune's named layer: the lexer, parser, named AST, the pretty-printer (core -> named surface), and name resolution (named surface -> locally-nameless core).
Package surface is Rune's named layer: the lexer, parser, named AST, the pretty-printer (core -> named surface), and name resolution (named surface -> locally-nameless core).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL