Skip to content

Latest commit

 

History

History
113 lines (93 loc) · 6.73 KB

landscape.md

File metadata and controls

113 lines (93 loc) · 6.73 KB

Where ClojureWasm sits — a map, not a scoreboard

Clojure runs in a lot of places now. Each runtime below was built for a different job and is excellent at it. This page is a map of where they sit, not a ranking — ClojureWasm is one young entry exploring the WebAssembly corner, and the only numbers it claims are its own measured ones.

Runtime Host / where it shines Distribution
Clojure (JVM) The JVM. Mature, complete, an enormous ecosystem. Runs on a JVM.
Babashka / SCI GraalVM native / JS. Fast-starting scripting, glue. Self-contained binary.
ClojureScript JavaScript. Front-end and the Node ecosystem. JS host.
jank LLVM / C++. Native code and seamless C++ interop. Native.
ClojureDart Dart / Flutter. Mobile and cross-platform UI. App bundles.
jolt Chez Scheme. A self-hosted compiler to native code, and C-ABI shared libraries. Self-contained binary.
clojurust Rust. Cranelift JIT, AOT to native, Rust libraries as cdylib plugins. Native binary.
ClojureWasm Zig. Embeds a Wasm engine so Clojure calls modules from Rust/Go/C (polyglot FFI). ~8.1 MB native binary, starts in ~6 ms.

The other rows are respectful summaries of what each runtime is known for, not evaluations. The ClojureWasm row lists only its own measured figures; it does not claim to beat anything.

What "the WebAssembly corner" means here

Two things, which are easy to conflate — and only the first one is shipped:

  1. ClojureWasm embeds a WebAssembly engine. A Clojure program can load a sandboxed module compiled from Rust, Go, Zig, or C and call it like a namespace. WebAssembly becomes an FFI: other languages' libraries become callable from Clojure, in-process and sandboxed. (See docs/examples/wasm/.) This is the angle ClojureWasm is exploring, and it has no obvious equivalent in the other runtimes.
  2. ClojureWasm does not ship as a .wasm. The runtime compiles for wasm32-wasi, and CI keeps that target compiling, but no release carries the artifact. That build is single-threaded: no futures or agents, no network, no cljw.ffi, no embedded engine. cljw build produces a native binary with an embedded bytecode payload, not a .wasm. The value representation and garbage collector use linear memory, so the build does not interoperate with WasmGC-based languages.

Compiling Clojure source into a WebAssembly component is a separate project: ClojureWit, in the upstream org.

The sibling: clojurust (cljrs)

clojurust is a Rust-hosted Clojure dialect by the same maintainer, with a Cranelift JIT and native AOT. Neither hosts the other (cljrs cannot host a component), but they share three things:

  • The C FFI. cljw.ffi and clojure.rust.ffi are one API over dlopen, so a portable adapter is one reader conditional (docs/examples/ffi/).

  • The Clojure surface. cljw reads .cljc under {:cljw :clj :default} and cljrs under {:rust}, so a host-free .cljc runs unchanged on the JVM, on cljw and on cljrs. Conditionals are needed only where a kernel touches a host. jolt runs the same file too (measured); it also reads :clj, so its :jolt branch goes before :clj.

  • The Rust library. The same crate serves cljw as a zero-import Wasm component and cljrs as a native cdylib. Measured on a Groth16 prover (2026-09-12): identical 128-byte proofs and verdicts on both; cljrs ran it in 1.29 s (debug build) with 212 environment variables, the filesystem and the clock in reach, cljw in 9.17 s with none of them. A program chooses speed or confinement per deployment, without a rewrite; the per-runtime adapter absorbs shape differences (a WIT result lifts to [:ok v] / [:err e], a trap reports as the capability answer it is) and the shared program keeps the semantics.

Hosting, and being hosted

ClojureWasm is a host. It loads two kinds of foreign code:

  • Wasm modules and WIT components, sandboxed, under a fuel and memory budget (cljw.wasm).
  • Native shared libraries, unsandboxed, through dlopen (cljw.ffi).

It is not yet a guest. cljw build produces an executable and there is no shared-library build, so a C, Rust or .NET program cannot link it. The wasm32-wasi build above is the one route in: any Wasm host could run it, cljw included, but it is unreleased.

jolt covers that direction: jolt build --library compiles a Clojure project into a shared object with its runtime inside, entered through jolt_library_init and jolt_lookup. The two meet at the C ABI. A jolt library is native code with ambient authority, so it loads through cljw.ffi, not through the sandbox. examples/ffi/jolt calls one: ffi/function-at over the pointers jolt_lookup returns.

A note on respect

Babashka and SCI made JVM-free Clojure scripting fast and ordinary; jank is doing the hard work of native Clojure with C++ interop; ClojureDart put Clojure on Flutter; ClojureScript has carried Clojure on the front-end for over a decade; and the JVM remains the complete, production-proven home of the language. ClojureWasm exists because of the path those projects cleared, and it is exploring one more corner — not competing for the others' ground.

Sources