Rationale

Rationale

Why Chez Scheme?

Jolt's main goal is a drop-in replacement for JVM Clojure with fast startup and a light memory footprint. Since both languages share the same Lisp roots, the core semantics of Clojure map seamlessly to Scheme, avoiding the impedance mismatch associated with forcing functional paradigms onto the JVM. Chez brings a mature JIT compiler that can aggressively optimize emitted code while maintaining a reasonably small runtime footprint. You also get a highly tuned generational garbage collector that is practically purpose-built to handle the rapid allocation and deallocation of immutable data structures inherent to Clojure workflows. Finally, Chez runs natively across a wide variety of operating systems, so you get lean native binaries with instant startup times rather than hauling around a heavy Java environment.

Deploying or distributing a Jolt app means building a binary for the target platform. Users don't need a JVM installed, and you don't even need Chez Scheme installed to develop using Jolt, because it produces its own self-contained jolt binary. It's also possible to create a Jolt library that can be embedded in C, C++, or Rust projects, providing a high-level Clojure API and interactivity via nREPL on top of performant native code.

The JVM Problem

While the JVM is an incredibly powerful piece of engineering, it is also one of the biggest reasons developers cite when passing on Clojure. The Java runtime evolved in an era of enterprise architecture, designed for massive application servers that would greedily consume all available host memory and run continuously for months on end, making long JVM startup phases and heavy initial resource footprints perfectly acceptable trade-offs. That monolithic design clashes with how modern web applications are typically built: fleets of isolated containers that boot instantly and can dynamically scale horizontally to handle sudden traffic spikes. Hauling around a warmed-up Java environment just to run a lightweight standalone service goes directly against that principle.

Existing Ecosystem and Java Shims

Several Clojure dialects already exist, and Jank in particular is well along toward a full Clojure implementation focused on core language compatibility. Unfortunately, most existing Clojure libraries rely on some JVM interop, meaning they will not run on these dialects without modification. The key realization behind Jolt is that most libraries don't actually use much of the Java standard library surface. Once you map out a few packages like java.io, java.time, and a few others, you can run a large portion of the current Clojure stack without having to reimplement or port it.

Today Jolt supports a number of popular Clojure libraries. It is already possible to build a fully fledged Ring app using Ring, Reitit, Selmer, and HoneySQL. Some libraries, like Reitit, drop down to Java, but Jolt makes it possible to provide shims using Jolt libraries. If you need access to a particular Clojure library that doesn't have shims available in the core language, you can always add them yourself. In many cases this doesn't need to be done from scratch, as you can leverage mature Scheme or C libraries for the underlying functionality and write a bit of glue code to expose it as a Java API.

Jolt uses deps.edn for managing dependencies and aliases largely the same way as regular Clojure. The familiar nREPL workflow is fully supported: you can start a Jolt app, jack in from your favorite editor, and develop it just as you would on the JVM. nREPL can also be embedded in compiled release binaries.

Conformance

As part of the project, Jolt is building out a conformance spec, EBNF, and RFCs to map out what actually constitutes Clojure. The clojure-test-suite from the Jank team has been invaluable for getting bootstrapped and having confidence that core language semantics such as the reader, special forms, and the bulk of clojure.core are correct. However, it stops short of the JVM host contract that many libraries depend on, so most of the effort in building the corpus went into closing that gap. The suite sources every expected value from reference JVM Clojure and currently holds around 3,500 cases, each tagged as either portable (:common, something any faithful Clojure dialect must satisfy) or host-dependent (:jvm, exercising interop). A certification step re-evaluates the whole corpus against real Clojure to catch unclassified divergences, ensuring that the contract doesn't quietly drift.

Under the Hood

Jolt mirrors nine java.* packages commonly used by popular Clojure libraries: java.io, java.lang, java.util, java.time, java.math, java.net, java.nio, java.sql, and java.text, with roughly a thousand method and field implementations across those packages. With no JVM underneath, class identity has to be synthesized using a hierarchy graph to back instance? and (class x), and raw Chez runtime errors need to be mapped onto a faithful JVM exception hierarchy so that catch dispatch works correctly.

The real test was running the actual test suites for individual libraries to discover subtle implementation quirks and Java host interactions. Porting spec.alpha, core.logic, core.async, test.check, tools.reader, rewrite-clj, and a few dozen others shook out most of the edge cases: data readers returning code forms, namespaced-map literals, *print-length* and *print-level*, protocol methods merging across deftype and reify, and even soft and weak references with genuine GC eviction wired through Chez's weak pairs and guardians.

In terms of footprint, a minimal binary compiles to around 13 MB without optimizations and just 8 MB when direct linked. Performance is currently comparable with JVM Clojure in a number of benchmarks, having parity or being around 2× slower on most, while only being around 7× worse in a couple of cases. This was made possible entirely by the mature Chez runtime. And just as you can drop down to Java with regular Clojure, Jolt lets you do the same with Scheme or even C FFI for cases where you need peak performance.

Why Not Just Wait for Leyden?

Project Leyden will address the notorious startup times and memory footprint issues associated with the JVM, but Jolt can push optimization much further by leveraging tricks like the full program optimization seen in Stalin Scheme. While both Leyden and Stalin rely on closed-world constraints, they have entirely different end goals. Leyden primarily focuses on shifting computation from runtime to build time, letting AOT compilers perform aggressive dead code elimination along with devirtualization and monomorphization. But Leyden cannot achieve the same type of structural erasure that Stalin does because it must respect Java semantics. A Stalin-style optimizer is entirely free to scalarize objects or bypass standard garbage collection paths when it proves an object is short-lived.

Building on Chez Scheme also sidesteps architectural baggage that Java simply cannot shed, the most obvious example being proper tail call optimization. Scheme mandates TCO by definition, so compiling Clojure down to Scheme provides infinite mutually recursive functions for free and embraces functional programming paradigms that the JVM actively fights against. The memory model and native interoperability provide another important divergence. Even with Leyden shrinking the JVM footprint, every value still carries the weight of a Java object header. Chez Scheme runs with much lower overhead and boots in microseconds. From Scheme you can manipulate raw C pointers and structs directly, wrapping high-performance libraries without the serialization and context-switching tax that JNI and Panama incur at the managed boundary.

There is also the reality of numeric computing. The JVM forces dynamic types into boxed objects, creating immense garbage collection pressure during tight mathematical loops unless you rely heavily on primitive type hinting. Scheme implementations utilize techniques like NaN boxing or tagged pointers to represent dynamic types directly in CPU registers, allowing dynamic mathematical operations to run much closer to the metal. Importantly, achieving Leyden-level performance with the JVM relies on an ahead-of-time compilation phase that destroys the interactive REPL experience that makes Clojure so powerful in the first place. Jolt can leverage Chez Scheme to generate highly optimized native machine code while retaining dynamic binding. You get the execution profile of a tightly compiled binary while keeping the interactive soul of Clojure intact. And you can still do AOT to get even better performance when interactive features aren't needed.

Jolt also supports tree shaking modeled on the ClojureScript compiler. Tracing the call graph and eliminating namespaces and functions that aren't referenced anywhere makes for a lean release binary.

Desktop and Native Interop

The bigger long-term benefit comes in the form of unlocking direct access to the Scheme and C ecosystems. Currently, the most common approach to building desktop applications in Clojure is to use wrappers around Java UI frameworks and accept the bloat that comes from a Java runtime. Bringing Clojure to Chez Scheme sidesteps the problem, allowing you to directly bind to native C libraries like GTK through a foreign function interface. Even though JNI and Project Panama exist, they necessarily introduce friction and overhead when crossing the boundary from managed memory to native pointers. From Scheme, the C boundary becomes practically invisible.

Jolt provides glimmer library for developing desktop applications which uses Reagent-style reactive atoms to drive GTK components. The reactive data model has proven itself to be an extremely effective tool in ClojureScript, and wrapping GTK with a Reagent-style API brings these same ergonomics to a native desktop environment. The application ends up having a far smaller memory footprint than anything running in a JVM possibly could, and nREPL-driven development works here as well. This extends further to glimmer-gl for OpenGL contexts inside GTK windows. Driving an OpenGL pipeline directly from Clojure makes it easy to model complex visual state dynamically without sacrificing raw performance.