jank-lang/jank: a native Clojure dialect with C++ interop
jank is the native Clojure dialect with seamless C++ interop.
At a glance
- What is it?
- jank compiles a Clojure-compatible language through LLVM so that Clojure code can call C++ headers directly. It is in alpha, and the documentation is a book rather than a README.
- Who is it for?
- jank is worth adopting only by engineers who want Clojure semantics on a native runtime and are willing to work against an alpha. If you need a stable dependency for production services today, stay on the JVM.
- Can I use it commercially?
- Yes, with conditions. MPL-2.0 is a weak copyleft licence: you can use it inside commercial and closed-source software, but if you distribute changes to its own files, you must publish those changes under the same licence.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The gap jank is trying to close
Clojure's default host is the JVM and its interop target is Java. That is a deliberate design choice, and it is why Clojure code can call into any Java library without a foreign function layer. It also means the runtime carries a JVM, startup is not instant, and calling into native libraries requires JNI or a separate binding project. jank takes the opposite position. The README states that jank's host is LLVM and its interop is with C++, and that the project aims to be strongly compatible with Clojure. So the target user is someone who already likes Clojure's value-oriented, functional style but wants the native runtime and performance characteristics of C++ rather than a managed VM. The repository topics list clojure, compiler, cpp, functional-programming, jit, language, lisp, llvm, native and programming-language, which matches that framing. This is not a general-purpose scripting language aimed at beginners. It is aimed at people who write C++ and wish they could write Clojure against the same libraries.
How the C++ interop actually works
The mechanism visible in the README is a namespace form that pulls C++ headers into the Clojure namespace. The example uses (:include "boost/filesystem.hpp") inside the ns declaration, followed by (:refer-global :rename {boost.filesystem.file_size file-size}). That second form reaches into a C++ namespace, picks out a symbol, and gives it a Clojure-side name. After that, file-size is callable like any other function. Arguments still need types the C++ side understands, which is why the example wraps the path with (cpp/cast std.string file-path). Exceptions cross the boundary too: the code catches boost.filesystem.filesystem_error and reads (.what e) from it. The result is an ordinary Clojure map with :path and :size, or :path and :error. Nothing here is a binding generator or an FFI stub you write by hand. The compiler is doing the work, which is consistent with the repository having a compiler+runtime directory and topics that include jit and llvm. The practical consequence is that the C++ types become part of your program's type surface, and the compiler has to know about them at compile time.
Installing jank and calling a C++ function
The README does not contain install instructions. It points readers to the jank book at book.jank-lang.org and states that jank is currently in alpha. The repository layout gives two other places to look: flake.nix and flake.lock at the top level, and a bin directory. If you use Nix, the flake is the entry point the repository provides for a reproducible build. The commands below reflect that layout rather than a documented install procedure, so treat them as a starting point and check the book for the supported path.
git clone https://github.com/jank-lang/jank.git
cd jank
nix developOnce you have a working compiler, the first real use is the filesystem example from the README. It is short enough to type out and it exercises both the include form and the exception path.
(ns my.app.filesystem
(:include "boost/filesystem.hpp")
(:refer-global :rename {boost.filesystem.file_size file-size}))
(defn file-info [file-path]
(try
(let [bytes (file-size (cpp/cast std.string file-path))]
{:path file-path
:size bytes})
(catch boost.filesystem.filesystem_error e
{:path file-path
:error (.what e)})))The README shows two calls and their expected results. Calling (file-info "/etc/passwd") returns {:path "/etc/passwd", :size 4025}. Calling (file-info "/root/.bash_history") returns a map with :path and an :error string reading "boost::filesystem::file_size: Permission denied [system:13]: \"/root/.bash_history\"". If you see a size map for the first and an error map for the second, the interop layer, the cast, and the exception translation are all working.
Alpha status is the real constraint
The README says plainly that jank is currently in alpha and sends readers to the book for details. The most recent release listed is from 2023-01-29, while the last push to main was on 2026-09-21. That combination tells you something specific: development is happening on the main branch, and the tagged release channel has not been the way the project ships for a long time. If you depend on versioned releases, you are depending on something that has not moved in years. If you build from main, you are depending on whatever state the branch is in. Neither is a reason to avoid the project, but both change how you would pin it. The other structural limit is the one the design implies. Because interop happens at compile time through headers, your build environment needs the C++ headers you reference, and the compiler needs to understand them. That is a heavier requirement than calling a Java class by name. A team that cannot control its C++ toolchain and header paths will find this harder than the JVM equivalent, not easier.
Where jank is the wrong tool
If your goal is to ship a service this quarter and you do not already have a C++ codebase or a native performance requirement, jank adds a compiler you have to build and an alpha you have to track, in exchange for benefits you will not use. The Clojure on the JVM path already gives you a mature runtime, a release process, and a library ecosystem that jank does not claim to replace. The README says jank aims to be strongly compatible with Clojure, which is a statement of intent about the language, not a claim about the ecosystem. Java interop code does not carry over. Any namespace that reaches into Java classes has to be rewritten against C++ equivalents or dropped. That is the migration cost, and it is not small for a codebase that leans on the JVM. The other case is a team without C++ build experience. The include and refer-global forms assume you can reason about headers, namespaces, and types on the C++ side. Without that, a compile error in the interop layer will be hard to read.
Compared with staying on the JVM
The honest alternative is Clojure on the JVM, and the difference is not a matter of polish. On the JVM, interop is dynamic and reflective in places, the runtime manages memory, and you get a garbage collector and a large set of existing Java libraries. In jank, the host is LLVM and interop is with C++, so the boundary is compiled and typed, and the C++ libraries you already have become reachable without a binding layer. The trade is that you inherit C++ build concerns: header availability, compile times, and the fact that a mistake on the C++ side surfaces as a compiler error rather than a runtime exception. A second alternative worth naming is writing the performance-sensitive part in C++ and calling it from Clojure through JNI. That keeps the JVM runtime and confines the native code to one module, at the cost of maintaining two languages and a binding interface. jank removes the binding interface, which is the part most teams find tedious. It does not remove the need to understand C++.
Licence and the cost of tracking main
jank is licensed under MPL-2.0. That is a file-level copyleft licence: modifications to files already covered by the licence stay under it, while larger works that combine jank with other code can be distributed under other terms. This is not legal advice, and if you are embedding jank in a product, the specific question is which files you modify and how you distribute them. Read the LICENSE file in the repository rather than relying on a summary. On upgrade cost, the repository layout is informative. There is a flake.lock, which pins the Nix inputs, and a HISTORY file, which suggests the project keeps a record of changes across versions. The release channel has not been updated since 2023-01-29 while main has moved, so an upgrade path built around releases does not exist in practice. You would be tracking main, reading HISTORY, and rebuilding the compiler when it changes. Budget for that before you put jank under anything that has to stay up.
Editorial conclusion
jank is worth adopting only by engineers who want Clojure semantics on a native runtime and are willing to work against an alpha. If you need a stable dependency for production services today, stay on the JVM. Before committing, read the jank book, confirm the compiler builds from the flake.nix or the documented build path, and check whether the C++ headers you depend on are reachable through the include and refer-global forms.
Frequently asked questions
What is jank-lang/jank used for?
jank is a general-purpose programming language that combines Clojure's functional, value-oriented style with a native LLVM runtime and C++ interop. The README frames it as a Clojure dialect on LLVM, and the example shows calling boost::filesystem from Clojure code.
Is jank-lang/jank ready for production?
The README states that jank is currently in alpha and directs readers to the jank book for details. The most recent listed release dates from 2023-01-29, while the last push to main was on 2026-09-21, so the release channel has not been the project's shipping path.
How does jank-lang/jank interoperate with C++?
A namespace can use (:include "boost/filesystem.hpp") to pull in a header and (:refer-global :rename {boost.filesystem.file_size file-size}) to bind a C++ symbol to a Clojure name. Arguments are converted with (cpp/cast std.string file-path), and C++ exceptions such as boost.filesystem.filesystem_error can be caught in Clojure.
What licence does jank-lang/jank use?
The repository is licensed under MPL-2.0. That is a file-level copyleft licence, so modifications to covered files stay under it while larger combined works can be distributed under other terms. The LICENSE file in the repository is the authoritative text.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/jank-lang-jank)