A pure scheme web programming tool

guenchi1 pts1 comments

Goeteia — a page that compiles itselfBuilt in pure Scheme<br>Everything beside this is rendered by Goeteia.<br>The Scheme below is compiled to WebAssembly in your browser and mounted live.<br>loading…<br>Runbooting the compiler…<br>The 3D title is a WebGL particle cloud — its glyphs, wave and colors are all in this source. Edit the dot-matrix rows, tweak the shader, press Run. This page carries the whole compiler (goeteia.wasm, ~70 KB gzipped, cached after first load); each recompile takes ~15 ms.

What's inside<br>Self-hosting, to the byte<br>The compiler is written in the Scheme subset it compiles. The self-hosted build recompiles itself and the output is byte-identical — the fixpoint is checked in CI fashion on every change, and every test runs through both stages.

Native Wasm GC objects<br>Fixnums are unboxed i31refs, pairs and records are GC structs, eq? is one ref.eq. No shadow heap in JavaScript: the host supplies two byte-stream imports and nothing else.

Hygienic macros<br>syntax-rules and procedural syntax-case with fenders, nested ellipses and datum->syntax, running in a compile-time interpreter with hygiene by renaming.

Real closures, real tail calls<br>Typed function references with a fast per-arity entry and a generic entry per closure — variadic procedures and apply are cheap, and every tail call is a return_call. A 100M-iteration loop runs in constant stack, in ~150ms.

The numeric tower<br>Fixnums promote to bignums on overflow (checked inline, two bit-compares), flonums have contagion, rationals and complex are exact, and float literals are encoded to IEEE-754 by pure Scheme — so both hosts emit identical bytes.

call/cc & dynamic-wind<br>Escape continuations ride the Wasm exception-handling proposal: capture is O(1), the normal path costs one try block, and winders unwind inner-to-outer on the way out.

A reactive web stack<br>(web sx) templates over fine-grained (web reactive) signals, an (web html) renderer, and a (web js) FFI that reaches straight into the host — this page is built with it.

Libraries<br>R6RS-style (library ...) files with (import (math utils)) resolution, dependencies first; exports are advisory because unused code is pruned anyway.

Quick start<br>$ git clone https://github.com/guenchi/Goeteia<br>$ cd Goeteia<br>$ ./run-tests.sh # every test, both compiler stages<br>$ ./build-self.sh # rebuild the compiler with itself

$ echo '(define (fact n) (if (zero? n) 1 (* n (fact (- n 1)))))<br>(fact 20)' > fact.ss<br>$ node rt/compile.mjs goeteia.wasm fact.ss fact.wasm<br>$ node rt/run.mjs fact.wasm<br>2432902008176640000Compiled modules run on any engine with Wasm GC and tail calls: Node 22+, current Chrome / Firefox / Safari, wasmtime. Bootstrapping from source needs Chez Scheme; the checked-in compiler wasm works without it.

wasm fact scheme goeteia compiler pure

Related Articles