Files
bara-lang/books/clojure-book/en/05-clojure-nim.md
T
dimgigov 23252826a0 feat: add Clojure/Nim chapter to the book + restructure for GitLab
- New chapter 05-clojure-nim.md (EN + BG) covering:
  - Native compilation pipeline (Clojure → Nim → C → binary)
  - AI-powered development (error explanation, code generation)
  - JSON REPL for AI agents
  - loop/recur with real TCO
  - Cross-compilation: JS, shared libs, WASM
  - Persistent data structures (HAMT)
  - Concurrency: atoms, agents, channels
- Updated book README.md with Clojure/Nim focus
- Added Clojure/Nim terms to subject indices (EN + BG)
- Removed books/ from .gitignore so it can be pushed to GitLab
2026-05-08 23:32:11 +03:00

6.6 KiB

Clojure/Nim: Native Clojure

The same language you love. Compiled to native binaries. No JVM required.


What is Clojure/Nim?

Clojure/Nim is a complete Clojure dialect that compiles to native machine code via the Nim compiler. It is not an interpreter — it is a real compiler with a full optimization pipeline:

Your .clj file
      ↓
  Reader (EDN parser)
      ↓
  Macro expansion (defmacro, syntax-quote, ->, ->>)
      ↓
  Emitter (Clojure AST → Nim source)
      ↓
  Nim Compiler → C code
      ↓
  C Compiler → Native binary

The result is a single executable file, often under 1 MB, that starts instantly with no JVM warmup.

Why Native?

JVM Clojure Clojure/Nim
Needs Java runtime installed Self-contained binary
~2-5 second startup time Instant startup
~100-300 MB memory footprint ~1-10 MB
JIT compilation pauses Ahead-of-time, predictable
Great for long-running servers Great for CLI tools, embedded, WASM

This is not a toy. It is a production compiler with 276+ tests, persistent data structures (HAMT), core.async channels, and an AI-assisted workflow.


Installation

git clone https://gitlab.com/balvatar/lisp-nim.git
cd lisp-nim
make build
make check   # run all tests + examples

Requirements: Nim ≥ 2.0, GCC or Clang, make.

That is it. No Java. No Leiningen. No deps downloads that take ten minutes.


Your First Program

Create hello.clj:

(println "Hello from native Clojure!")
(println (+ 1 2 3 4 5))

Run it:

$ ./cljnim run hello.clj
Hello from native Clojure!
15

Notice: the program was parsed, macro-expanded, emitted as Nim, compiled to C, compiled to machine code, and executed — all in under a second.


The REPL: Two Modes

Human REPL

$ ./cljnim repl
user> (defn square [x] (* x x))
user> (square 7)
=> 49
user> (atom 42)
=> (atom 42)
user> :ai function for fibonacci
🤖 Thinking...
💡 AI Suggestion:
   (defn fib [n]
     (loop [a 0 b 1 i 0]
       (if (= i n) a (recur b (+ a b) (inc i)))))

JSON REPL (for AI Agents)

$ ./cljnim repl --json
{"status":"ready","ns":"user","mode":"json"}

> {"op":"eval","form":"(+ 1 2 3)"}
{"status":"ok","result":{"printed":"6"},"meta":{"ms":12}}

> {"tool":"cljnim/eval","args":{"form":"(defn greet [name] (str \"Hello \" name))"}}
{"status":"ok","result":{"type":"var","name":"greet"},...}

The JSON REPL is designed for programmatic interaction. Every operation has structured input and output. AI agents can discover capabilities, evaluate code, inspect definitions, and batch-process forms without parsing human-readable text.


AI-Powered Development

Clojure/Nim is the first Clojure implementation built with AI assistance as a first-class feature.

Error Explanation

When compilation fails, the compiler asks an AI for help:

$ ./cljnim run broken.clj
Compilation failed
Error: identifier expected, but got 'keyword var'

💡 AI Suggestion:
   The error occurs because `var` is a reserved keyword in Nim.
   Fix: Rename the function to `my-var`.

Code Generation

Generate idiomatic Clojure from a description:

$ ./cljnim ai "function that filters even numbers"
(defn filter-even [coll]
  (loop [remaining coll result []]
    (if (empty? remaining)
      result
      (let [x (first remaining)]
        (recur (rest remaining)
               (if (even? x) (conj result x) result))))))

Setup

export DEEPSEEK_API_KEY="sk-..."
# or OPENAI_API_KEY, or MIMO_API_KEY

loop / recur: Real TCO

Unlike JVM Clojure (which uses recur to avoid stack overflow but still runs on a stack-based VM), Clojure/Nim compiles loop/recur to a native while loop:

(defn factorial [n]
  (loop [acc 1 i n]
    (if (= i 0)
      acc
      (recur (* acc i) (dec i)))))

This generates efficient C code with no function calls. It is genuinely O(1) stack space.


Cross-Compilation Targets

Clojure/Nim can target multiple platforms from the same source:

Native Binary (default)

./cljnim run program.clj

JavaScript

./cljnim compile program.clj program.nim
nim js -o:program.js program.nim
node program.js

C Shared Library

./cljnim compile-lib program.clj program.nim
nim c --app:lib -o:libprogram.so program.nim

WASM (experimental)

# Via Zig or Emscripten
nim c -d:release --cpu:wasm32 --os:linux program.nim

This is something JVM Clojure simply cannot do.


Persistent Data Structures

Clojure/Nim implements real Hash Array Mapped Trie (HAMT) vectors and maps:

(def v (vector 1 2 3))
(def v2 (conj v 4))
;; v  => [1 2 3]
;; v2 => [1 2 3 4]
;; Structural sharing: O(log₃₂ n) updates, not O(n) copies

The runtime uses the same algorithms as Clojure/JVM (32-way branching, path copying, tail optimization), but compiled to bare metal.


Concurrency

Atoms

(def counter (atom 0))
(swap! counter inc)    ;; => 1
(reset! counter 100)   ;; => 100
(deref counter)        ;; => 100

Agents

(def state (agent 0))
(send state + 10)
(await state)
(deref state)          ;; => 10

Channels (core.async)

(def ch (chan 10))
(put! ch 42)
(take! ch)             ;; => 42
(close! ch)

All concurrency primitives work in both the compiled runtime and the in-memory interpreter.


Macros That Work

(defmacro unless [condition body]
  `(if (not ~condition)
     ~body))

(unless false
  (println "This prints!"))

Threading macros, when-let, cond, doto, some-> — all implemented as real Clojure macros, expanded at compile time.


When to Use Clojure/Nim

Use Case Clojure/JVM Clojure/Nim
Large web services ⚠️ (early stage)
CLI tools ⚠️ (slow startup) (instant)
Embedded systems
WASM / browser ClojureScript (native WASM)
Shared libraries
AI agent scripting (JSON REPL)
Learning Clojure (no JVM needed)

Further Reading


Clojure/Nim is proof that you do not need a virtual machine to write elegant, functional, immutable code. You just need a good compiler.