Asi jank

jank-lang: native Clojure on LLVM with seamless C++ interop. Use when writing native Clojure, bridging C/C++ libraries, or applying SICP Ch4-5 metalinguistic abstraction concretely.

install
source · Clone the upstream repo
git clone https://github.com/plurigrid/asi
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/plurigrid/asi "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/jank" ~/.claude/skills/plurigrid-asi-jank-82df75 && rm -rf "$T"
manifest: skills/jank/SKILL.md
source content

jank

Write Clojure for humans, compile to LLVM IR for machines.

Native Clojure on LLVM. Seamless C++ interop. 3,117 stars. Alpha January 2026. Jeaye Wilkerson.

Use: native perf, C/C++ libs, AOT binaries, SICP Ch4-5 concretely. Don't use: pure JVM (

clojure
), scripting (
babashka
), browser (
shadow-cljs
).

Theoretical Foundations

SourceConceptjank
SICP 1λ, higher-order fnsfns → LLVM IR; closures = GC'd C++ objects
SICP 2Abstraction barriersPersistent data in C++;
convert<T>
= barriers
SICP 3State, concurrency
future
(#674); atom/ref/agent; thread-safe
SICP 4Metacircular evaljank IS it — Clojure evaluating Clojure → native
SICP 5Register machines, GCLLVM IR = registers; Boehm GC = 5.3; AOT = 5.5
SDF 1-2Combinators, DSLs
cpp/raw
,
cpp/cast
,
cpp/box
compose;
cpp/
= sub-DSL
SDF 3-4Generic ops, matching
convert<T>
traits + Clang overload resolution
SDF 7PropagatorsBidirectional: jank string ↔
std::string
SDF 8DegeneracyREPL → JIT → AOT = multiple paths, same result

Compile AND interpret simultaneously. C++ interop = escape hatch into the machine.

Pipeline

Statically typed. Zero overhead. No reflection. Four stages per interop call:

  1. Wrapper — C++ helper (unpack args, box return)
  2. convert<T>
    — trait-resolved type conversion at compile time
  3. Pointer adj
    this
    , const/ref for members
  4. IRCppInterOp → LLVM IR → linked

Bootstrap: Phase 1 builds compiler (C++/LLVM), Phase 2 compiles jank's stdlib with itself.

Interop

::
.
(
std::string
std.string
). Clojure resolves first;
cpp/
disambiguates.

;;; HEADERS — cpp/raw: global scope, returns nil, workaround escape hatch
(cpp/raw "#include <cstdlib>")
(cpp/raw "struct vec2 { float x{}, y{}; };
          vec2 operator+(vec2 const &l, vec2 const &r)
          { return { l.x + r.x, l.y + r.y }; }")
;; -I <path> -D FOO -L <path> -l <lib>
;; :jank {:include-dirs [...] :library-dirs [...] :linked-libraries [...]}

;;; FUNCTIONS — overloads, implicit conv, void→nil, variadics, templates
(printf "result: %d\n" (rand))
(let [u (getenv #cpp "USER")] (println u))     ; #cpp = unboxed C++ value

;;; MEMBERS — .foo call, .-foo field (public, const/ref-qual aware)
(let [s (cpp/cast std.string "meow")] (.size s) (.substr s 1 3))
(.-npos std.string)

;;; OPERATORS — 45: + - * / % == != < > <= >= && || ! & | ^ ~ << >> = += [] * -> ++ --
(let [ab (cpp/+ (vec2. #cpp 1.0 #cpp 2.0) (vec2. #cpp 4.0 #cpp 3.0))]
  (println (.-x ab) (.-y ab)))

;;; TYPES
#cpp 42  #cpp 3.14  #cpp "hello"               ; unboxed int, double, char[]
cpp/true  cpp/false  cpp/nullptr                ; native C++ bool/null
(cpp/value "std::numeric_limits<int>::max()")   ; complex exprs
(cpp/type "std::map<std::string, int (*)(int)>"); templates
cpp/int**                                       ; pointer types
;; enums (scoped+unscoped), function ptrs, functors/lambdas all work

;;; CAST
(cpp/cast cpp/int (rand))                       ; static_cast + convert<T>
(cpp/unsafe-cast (cpp/type "unsigned char*") s) ; reinterpret_cast

;;; MEMORY — bdwgc GC; cpp/delete eager for RAII
(let [p (cpp/new cpp/int (cpp/int. 500))] (assert (= 500 (cpp/* p))) (cpp/delete p))

;;; ARRAY + BOX
(cpp/aget arr idx)
(let [b (cpp/box (cpp/new my_db.conn #cpp "localhost:5758"))]
  (.query (cpp/unbox my_db.conn* b) "SELECT 1")) ; compile-time type check

;;; CONVERT TRAIT — custom: specialize jank::runtime::convert<T>
;;   template <> struct jank::runtime::convert<MyType> {
;;     static MyType from(object_ptr o) { ... }
;;     static object_ptr to(MyType const &v) { ... }  };

Examples

;; JSON pretty-printer (header-only nlohmann/json)
(cpp/raw "#include <fstream>")  (cpp/raw "#include \"json.hpp\"")
(defn -main [& args]
  (let [f (std.ifstream. (cpp/cast std.string (first args)))]
    (println (.dump (nlohmann.json.parse f) 2))))

;; zlib compression (-l z)
(cpp/raw "#include <zlib.h>")
(defn compress-str [input]
  (let [src (cpp/cast (cpp/type "const Bytef*") input)
        n   (cpp/cast cpp/uLong (count input))
        dn  (compressBound n)
        dst (cpp/new (cpp/type "Bytef") dn)]
    (compress dst (cpp/& dn) src n) (cpp/box dst)))

;; FTXUI hiccup → C++ flexbox
(render-hiccup [:vbox
  [:hbox [:text "NW"] [:filler] [:text "NE"]] [:filler]
  [:hbox [:filler] [:text "center"] [:filler]] [:filler]
  [:hbox [:text "SW"] [:filler] [:text "SE"]]])

Usage

jank repl                                       # eval/apply
jank run app.jank                               # JIT
jank -I vendor -l z run app.jank -- args        # + native libs
jank compile app.jank -o app                    # AOT binary
lein new org.jank-lang/jank proj && lein run    # Leiningen
lein compile                                    # AOT → ./a.out
zerobrew install jank-lang/jank/jank             # install macOS (also: apt, yay, nix)

Status (Jan 2026)

DoneMissing
Interop: headers, fns, members, ctors, cast, box, 45 opsRecords, Protocols
#cpp
,
cpp/value
,
cpp/aget
,
cpp/unsafe-cast
, enums, lambdas
Types in jank syntax
PCH, AOT, two-phase, deferred compilation (+50%)CIDER (Clang bug)
future
, thread safety, regex, UUID, instant, BigDecimal
Library parity: 57%
nREPL + imgui (Kyle Cesare); zerobrew/apt/AUR/nixSyntax parity: 93%

Contributors: Jeaye Wilkerson + Saket (interop), Monty (compiler/distro), Jianling (catch/stdlib), Shantanu (REPL/tests/bigint), Kyle Cesare (nREPL/imgui), djblue, pfeodrippe, E-A-Griffin, Samy-33, cjbarre.

Δ Clojure

Clojurejank
ClasspathModule path
(hash-map)
→ array-map
Always hash-map
aget
fn
aget
special form
Nested
require
No (like CLJS)
import
cpp/raw #include
defrecord
/
defprotocol
Not yet
/ 1 0
→ exception
UB
#js
#cpp

Ecosystem

scripts/splitmix64.jank
— SplitMix64 → hue → Girard polarity → TAP.
jank run scripts/splitmix64.jank -- 42

scripts/neanderthal.jank
— ersatz Neanderthal (uncomplicate) syntax on Eigen via jank interop. BLAS L1/2/3:
dv
,
dge
,
dot
,
axpy
,
scal
,
mv
,
mm
. MPC primitives (
mpc-predict
,
mpc-cost
,
mpc-horizon
). Append-only log for promise chain resolution.
jank -I /path/to/eigen3 run scripts/neanderthal.jank

asi/ies/music-topos/lib/crdt_sexp_ewig.jank
— same PRNG + CRDT ops + Lager actions.

jank_interop.duckdb
commits
(is_interop),
pull_requests
,
repo_meta
.

Connections

SkillTrit
clojure
0Language family
sicp
0Ch4-5 realized
babashka
0Scripts ↔ native
zig
+1Complementary native
propagators
0SDF 7 bidirectional
rama-gay-clojure
0SplitMix64 shared

clojure(0) + jank(+1) + property-based-testing(-1) = 0 mod 3

Cat#

+1 PLUS | Prof | y (Yoneda) | Lan | #E847C0
— compilation ⊣ interpretation, mediated by REPL.

Refs

jank-lang.org | book | GitHub | interop blog 1 2 3 | SICP | SDF