Three Continuation Machines: Yin.vm/Yang, Gambit Scheme, and Ribbit
The Common Ancestor: Scheme’s First‑Class Continuations
The continuation, as captured by call/cc in Scheme, represents “the rest of the computation from this point onward.” It’s a first‑class value that can be stored, passed, and resumed. From this shared foundation, three projects have taken radically different paths.
Each answers a different question: What should a continuation‑based virtual machine do? Yin.vm/Yang says: Be a mobile, queryable stream processor. Gambit says: Run fast and portable. Ribbit says: Run everywhere, in the smallest footprint possible.
Design Philosophy
Their core goals dictate their architectures:
| System | Primary Goal | Key Metaphor |
|---|---|---|
| Yin.vm + Yang | Everything is data. Unify code, runtime state, and continuations as immutable, queryable datom streams. Enable mobile agents, introspection, and multi‑language semantics. | Stream‑based physics: the datom stream is the wave function; interpreters project different views. |
| Gambit Scheme | Performance and portability. Produce fast standalone executables via C compilation, with R4RS/R5RS compliance and green‑thread concurrency. | Classic Scheme engine: compile to efficient C, leverage native optimizations. |
| Ribbit | Extreme portability and compactness. Compile R4RS Scheme to 25+ host languages while removing unused code. Target embedded, educational, and polyglot environments. | Universal adapter: write once, run anywhere, with a tiny footprint. |
Architecture Deep Dive
How each system implements continuations, code representation, distribution, and interop:
| Dimension | Yin.vm + Yang | Gambit Scheme | Ribbit |
|---|---|---|---|
| Execution Model | CESK machine (Control, Environment, Store, Continuation) with persistent data structures. No hidden stack; continuations are explicit, reified control contexts. | Stack‑based execution via C compilation. First‑class continuations via heap‑allocated stack copying (call/cc). Green threads (chez‑threads). | Register‑based portable VM (RVM). Scheme source → compact bytecode interpreted by a tiny VM written in each host language. |
| Continuations | First‑class, serializable, migratable. Continuations are datoms capturing control state only; environments/stores live in external streams. Unifies functions, closures, and agents. | First‑class via stack copying. Efficient within a single process but not designed for serialization or network migration. Termite variant adds Erlang‑style message‑passing actors. | First‑class as required by R4RS. Implemented via heap‑allocated stack segments in the portable VM. No built‑in migration or serialization across hosts. |
| Code Representation | Universal AST (Abstract Semantic Graph) stored as datoms [e a v t m] in DaoDB. Queryable via Datalog. Multiple backends (stack, register, semantic) project the same datom stream. | Traditional AST compiled to C intermediate representation, then to native code. No persistent queryable representation after compilation. | Scheme source → RVM bytecode (compact, specialized). The compiler (rsc.scm) emits host‑language‑specific VM interpreters that execute the bytecode. |
| Distribution | Mobile agents: continuations carry capability tokens and migrate to data. Stream‑based IO; computation moves, data stays. Built for distributed, heterogeneous networks. | Single‑node; Termite variant adds actor‑model message‑passing across processes (Erlang‑style), but no continuation migration. | Single‑node per host. No built‑in distribution; focus is on cross‑language portability, not network‑transparent execution. |
| Language Interop | Yang is a multi‑language compiler collection (Clojure, Python, etc.). All compile to the same Universal AST, enabling seamless cross‑language function calls and data sharing. | Scheme‑only. Extensive FFI to C/C++/Objective‑C libraries. No semantic integration with other high‑level languages. | Host‑language primitives: can define primitives that interact directly with any of the 25+ host languages (e.g., embed Python or C code in Scheme). |
| Introspection | Full Datalog querying over AST, bytecode, continuations, environments, and stores. Time‑travel debugging, hot‑code reload, and AI‑assisted modification are native. | Limited introspection: debugger support, stack traces, and module reflection. No queryable runtime database. | Minimal introspection: the REPL provides basic evaluation, but no deep querying of runtime state or execution history. |
| Performance | Prioritizes flexibility and mobility. JIT compilation from datom streams to bytecode is possible; structural sharing enables fast snapshots. Not optimized for raw speed. | Optimized for speed: compiles Scheme to efficient C, leveraging native compiler optimizations. Lightweight green threads. | Prioritizes compactness over speed. The generated VMs are tiny (7 KB for a full R4RS REPL) but interpreted, so slower than native compilation. |
| Concurrency | Continuation scheduling as primitive; async/await, generators, actors, and backtracking are patterns over continuation manipulation. | Green threads (chez‑threads) and Termite’s actor model (message‑passing). No unified continuation‑based control‑flow. | Single‑threaded interpreter. No built‑in concurrency; relies on host‑language threading if available. |
| Storage Model | Immutable datoms in DaoDB (covering indexes: EAVT, AEVT, AVET, VAET). All state is append‑only streams. | Traditional mutable memory with garbage collection. No built‑in persistent storage or stream abstraction. | Ephemeral VM heap; no persistent storage. I/O primitives delegate to host‑language file operations. |
| Portability | Runs on JVM and Node.js via Clojure/ClojureScript. The Universal AST is platform‑agnostic, but execution requires a Clojure runtime. | Portable to any platform with a C compiler. Generates standalone executables for Linux, macOS, Windows, etc. | Extreme portability: targets 25+ host languages (C, JavaScript, Python, Prolog, POSIX Shell, x86 Assembly, etc.). Write once, run anywhere. |
Design Lineage
These systems emerge from different branches of the Scheme/Lisp family tree:
Gambit (1988) → Termite (actors) → Ribbit (2020s)
Evolution: fast Scheme‑to‑C → actor‑based concurrency → extreme portability & compactness
Yin.vm/Yang (separate lineage)
Influences: Clojure, Datomic, Plan 9, Smalltalk
Philosophy: streams, immutability, queryable knowledge graphsRibbit is a direct descendant of Gambit (both from Marc Feeley’s lab). Yin.vm/Yang draws from the Clojure/Datomic tradition, treating continuations as data in a global, versioned fact store.
Key Contrasts
Summary of tradeoffs:
| Aspect | Yin.vm/Yang | Gambit | Ribbit |
|---|---|---|---|
| Continuations | Datoms, mobile, lightweight | Stack copies, efficient but local | VM‑level, portable but not migratable |
| Code as data | AST is persistent, queryable datoms | AST discarded after compilation | Bytecode is compact, specialized per host |
| Distribution | Agents migrate with capability tokens | Termite actors message across processes | No distribution; cross‑language portability |
| Language interop | Semantic unification via Universal AST | FFI to C/C++/Objective‑C | Host‑language primitive embedding |
| Introspection | Full Datalog querying over runtime state | Debugger and stack traces | Basic REPL, no deep querying |
| Portability | JVM/Node.js | C compiler required | 25+ host languages, extreme portability |
| Size/Footprint | Moderate (Clojure runtime) | Moderate (generated C binary) | Extremely small (7 KB for R4RS REPL) |
When to Use Which
- Choose Yin.vm/Yang when you need distributed, queryable, multi‑language systems where code mobility and introspection are primary. Ideal for agent‑based simulations, collaborative coding environments, or systems that require time‑travel debugging and semantic versioning.
- Choose Gambit when you need high‑performance Scheme applications as standalone binaries, with green‑thread concurrency and a mature FFI. Great for servers, command‑line tools, or embedding Scheme in larger C/C++ projects.
- Choose Ribbit when footprint and host‑language reach matter most: educational tools, embedded scripting, polyglot research, or environments where installing a C compiler isn’t feasible. Perfect for “run this Scheme snippet in your browser, Python REPL, or Prolog interpreter.”
Future Directions
Could these designs inform each other? Imagine Ribbit’s multi‑host VM as a target for Yin.vm’s datom streams, enabling Yin agents to run on 25+ platforms without a Clojure runtime. Or Gambit’s Termite actors re‑expressed as mobile continuations, migrating across nodes while retaining their C‑level speed.
The deeper lesson: continuations are a universal semantic kernel (see Continuations as Universal Semantic Kernel). Whether you store them as datoms, copy them on the heap, or interpret them in a tiny VM, they remain the single primitive that can encode exceptions, async/await, generators, actors, and backtracking. The three systems simply optimize for different dimensions of the same core idea.
Conclusion
Yin.vm/Yang, Gambit, and Ribbit show three viable futures for continuation‑based VMs: mobile‑first, speed‑first, and portable‑first. Each serves a distinct need, yet all share the same ancestral insight: that capturing “the rest of the computation” as a first‑class value unlocks profound architectural simplicity.
Which future you choose depends on what you value most: moving computation, running fast, or running everywhere.
Learn More
- Yin VM documentation – the stream‑based CESK machine
- Yang compiler documentation – multi‑language to Universal AST
- Gambit Scheme – high‑performance Scheme‑to‑C compiler
- Ribbit on GitHub – compact, portable Scheme VM
- Continuations as Universal Semantic Kernel – why continuations unify control‑flow constructs
- Computation Moves, Data Stays – how Yin.vm keeps continuations lightweight
- Universal AST vs Assembly – low‑level form, high‑level semantics