}

A Playground for Gerbil Scheme

A Playground for Gerbil Scheme

One kind of side project I get a kick out of: building tools for languages I find interesting but that don't have the ecosystem gravity to attract them on their own. Last year it was Coalton, a typed Lisp that compiles through Common Lisp. This time it's Gerbil.

Why

Gerbil Scheme is one of those languages that deserves more attention than it gets.

Built on top of Gambit (which compiles Scheme to C, and then to native code), it adds a proper module system and pattern matching — and, most distinctively, an actor-oriented concurrency model. It's what you'd get if you took the "Scheme is elegant but impractical" complaint seriously and addressed it without abandoning the elegance.

The problem is the same problem every niche language has: the distance between "hearing about it" and "trying it" is too large. You have to install Gambit, then Gerbil, configure your environment, find the right invocation to start a REPL ... and by then the curious-but-uncommitted visitor has moved on.

I thought some sort of "playground" (and I always think of the classic Golang one here) makes that first step easier.

What

The Gerbil Playground at trygerbil.dev is a browser-based REPL that connects to a compiled Gerbil eval server running in its own container (tried to have it not be a batch executor where you write code and press "Run", but rather more of a conversational REPL).

Type an expression, see the result, define a function, call it. State persists, etc.

The interface has three panels: a terminal-style REPL at the bottom (the primary interaction), a scratchpad editor for multi-line code at the top left, and a session panel at the top right that shows your current bindings as they evolve. Everything evaluates into the same session.

There are some guided examples, from basic arithmetic through pattern matching, hash tables, higher-order functions, and Gerbil's actor primitives (the examples are scoped to what the playground can actually do well, right now).

How

Each REPL session gets its own container: a 46MB Alpine image with a statically linked Gerbil binary. The eval server is about forty lines of Gerbil that wrap eval with output capture, a five-second timeout, and a basic sandbox. It compiles to a single executable via gxc -exe, and the resulting binary links Gambit's runtime directly (so, no separate .so files to manage).

The whole stack runs on Cloudflare: "Pages" for the frontend, "Workers" for API routing and rate limiting, Durable Objects for session-to-container mapping, D1 for snippet storage. Containers are Cloudflare's relatively new offering (still in beta) but they turned out to be a good fit for this kind of "one process per user session" architecture. Containers sleep after a few minutes of idle and wake in about 200ms (steady-state eval latency should be under 3ms).

The statefulness problem is the interesting engineering bit. When a container sleeps, the Gerbil process is gone: all defined bindings evaporate. The frontend maintains a "shadow history" of every definition and replays it transparently when the container wakes (you don't notice it happened unless you're watching the network tab!). It's the kind of trick that's easy to describe but genuinely tricky to get right: you need to handle replay ordering, suppress output during replay, and gracefully degrade when replay fails.

What doesn't work

A compiled Gerbil binary can't do runtime (import ...) , the modules have to be compiled in. So the playground pre-loads :std/sugar (which gives you the hash literal, try/catch, and other conveniences) and :std/srfi/1 (the standard list processing library). This means users can't pull in arbitrary Gerbil modules, which limits the playground to a curated subset of the language ... I think that's the right tradeoff, at least to start with, can still show what Gerbil can do, which feels more important than exposing everything it can do.

Output capture from spawned threads doesn't work yet. When you spawn a thread and it calls displayln, the output goes to the container's stdout rather than flowing back to the REPL. The thread runs, the computation happens, you get the return value (you just don't see the side effects). This is a Gambit threading constraint (parameterize bindings are thread-local) and it's fixable, can have a workaround later.

The sandbox shadows dangerous operations at the Gerbil level (shell-command, file I/O, exit) but it's not watertight: Gambit's ## prefix can bypass the shadowing and reach the C runtime directly. The container is the real security boundary. This is fine for a playground where each user gets their own isolated process, but I mention it because the distinction between "sandboxed language" and "sandboxed environment" matters and is often elided.

Why bother

The honest answer is that I like building these. There's a satisfying challenge in taking a language that compiles to native code and making it feel immediate in a browser. The constraints are real ... you can't just ship a WASM build of Gerbil (Gambit's C backend doesn't target it), so you end up building actual infrastructure: containers, session management, replay systems.

Gerbil deserves to be known more. It occupies an unusual point in the language design space: a Scheme that takes systems programming seriously, with an actor-oriented concurrency model, proper modules, and native compilation. Most people who try it through the playground won't switch to it: but some of them will see the actor primitives, or the pattern matching, or the compiled performance, and hopefully some might think "oh, that's actually interesting" :-)

Try it

Head over to: trygerbil.dev