Skip to content

clojurewasm/ClojureWasm

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3,510 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ClojureWasm

ClojureWasm

A JVM-free Clojure runtime in Zig, with a WebAssembly FFI.

Note

ClojureWasm is not yet stable and is built by a very small team with limited resources. To keep that focus, Issues and Pull Requests are not being accepted right now. You are very welcome to read along, try it, and say hello in GitHub Discussions.

What it is

ClojureWasm is a Clojure runtime written from scratch in Zig and Clojure, with no JVM. It builds to a small native binary (arm64 / amd64) that starts in milliseconds. Its main feature is a WebAssembly FFI: from your Clojure code you can load a module compiled from another language — Rust, Go, Zig, C — and call it like an ordinary function. The idea is to stay in the Clojure world and still use what other languages have already built.

Features

  • Small and quick to start — about 3.8 MB, starting in ~5 ms, which suits short-lived, start-and-stop workloads (CLI tools, serverless, scripts).
  • A lot of everyday Clojure runsclojure.core plus a growing set of standard-library namespaces (clojure.string / set / walk / zip / edn / data.json / data.csv / math / pprint / test / tools.cli …).
  • A CIDER-compatible nREPLcljw nrepl and connect your editor to evaluate real Clojure live.
  • WebAssembly as an FFI(wasm/load "mod.wasm") then (wasm/call m "fn" …): a sandboxed module from any language, called like a namespace. The FFI runs JIT-compiled by default (the embedded zwasm engine), so a hot loop inside a module executes as native code.
  • WebAssembly components as namespaces(:require ["comp.wasm" :as c]) pulls a WIT-typed component in like a library; its exports become ordinary Vars, with arguments and results as plain Clojure data (see below).
  • Single-binary buildscljw build script.clj -o app compiles your program (and the runtime) into one self-contained executable.

Quickstart

Build the optimized, Wasm-enabled binary (needs Zig 0.16 — direnv allow loads it via Nix, or nix develop):

zig build -Dwasm -Doptimize=ReleaseSafe   # → ./zig-out/bin/cljw

Then (the examples assume cljw is on your PATH):

# Call a WebAssembly module compiled from another language, like a function
cljw -e '(wasm/call (wasm/load "examples/wasm/add.wasm") "add" 40 2)'   # => 42

# Evaluate an expression
cljw -e '(->> (range) (filter even?) (take 5))'     # => (0 2 4 6 8)

# Run a file
cljw script.clj

# A REPL — and an nREPL for CIDER / your editor
cljw
cljw nrepl --port 7888

# Compile a program to a single self-contained native binary
cljw build script.clj -o app

Require a Wasm component like a namespace

The headline feature. A WebAssembly component — a .wasm that carries its own WIT type signature — can be :require-d like a Clojure library. Its exports become ordinary Vars, and because the component describes its own types, arguments and return values are plain Clojure data with no hand-written glue.

Build a component from any language that targets the component model (here Rust, cargo build --target wasm32-wasip2), then require the .wasm by name:

(ns my.app
  (:require ["typed_payload.wasm" :as tp]))   ; required like a lib

(tp/process {:xs [3 4 5] :label "data"})
;; => {:xs [3 4 5 12], :label "data!"}

The WIT record arrived as a map, list as a vector, string as a string — and the parameter name survives as metadata, so the export reads like a normal fn:

(:arglists (meta #'tp/process))   ;; => ([input])

JVM Clojure calls Java and ClojureScript calls JavaScript; here the host is a language-neutral .wasm, so a function from Rust, Go, or C is indistinguishable from a Clojure one.

Try it live

  • Playgroundhttps://cw-playground.fly.dev — run Clojure in your browser, evaluated in-process by cljw; call sandboxed Rust / Go WebAssembly modules over the FFI. (source)
  • Bookshelf demohttps://cw-serverless-demo.fly.dev — a small multi-user bookshelf served end-to-end by cljw's own HTTP server, with SQLite and book-cover colours running in-process through the WebAssembly FFI, no JVM. (source)

Documentation

License

Eclipse Public License 2.0 — see LICENSE and NOTICE.

About

A lightweight Clojure runtime in Zig — call WebAssembly from Clojure to tap libraries written in any language.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors