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:

SystemPrimary GoalKey Metaphor
Yin.vm + YangEverything 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 SchemePerformance 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.
RibbitExtreme 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:

DimensionYin.vm + YangGambit SchemeRibbit
Execution ModelCESK 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.
ContinuationsFirst‑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 RepresentationUniversal 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.
DistributionMobile 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 InteropYang 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).
IntrospectionFull 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.
PerformancePrioritizes 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.
ConcurrencyContinuation 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 ModelImmutable 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.
PortabilityRuns 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 graphs

Ribbit 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:

AspectYin.vm/YangGambitRibbit
ContinuationsDatoms, mobile, lightweightStack copies, efficient but localVM‑level, portable but not migratable
Code as dataAST is persistent, queryable datomsAST discarded after compilationBytecode is compact, specialized per host
DistributionAgents migrate with capability tokensTermite actors message across processesNo distribution; cross‑language portability
Language interopSemantic unification via Universal ASTFFI to C/C++/Objective‑CHost‑language primitive embedding
IntrospectionFull Datalog querying over runtime stateDebugger and stack tracesBasic REPL, no deep querying
PortabilityJVM/Node.jsC compiler required25+ host languages, extreme portability
Size/FootprintModerate (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