Joker trades the Clojure standard library for a fast binary
Small Clojure interpreter, linter and formatter.
At a glance
- What is it?
- Joker is a Clojure dialect, linter and formatter written in Go, designed for scripting rather than speed, with lint and format modes that read Clojure and ClojureScript. It is licensed EPL-1.0, ships as a single static binary, and the README is explicit that many Clojure features it will never have.
- Who is it for?
- Adopt Joker if your job is reading and checking Clojure code from shell scripts, a pre-commit hook or CI, or if you want a Clojure-like REPL that starts without a JVM. Do not adopt it to run a production service: performance is a stated non-goal, and the author says plainly that there are Clojure features Joker does not and will never have.
- Can I use it commercially?
- Yes, with conditions. EPL-1.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 18 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Star history
- candid82/joker1,774 ★ · -1
| Repository | 2026-10-16 | 2026-10-23 |
|---|---|---|
| candid82/joker | 1.8k | 1.8k |
-1 Stars in the last 7 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
One binary, three modes, and no JVM on the command path
Joker is a single Go program that does three jobs: it interprets Joker, a Clojure dialect; it lints Clojure, ClojureScript and its own dialect; and it formats Clojure source and EDN data. The repository is a Go module, `github.com/candid82/joker`, with `main.go`, `repl.go`, `tasks.go` and platform specific `exiters.go`, `exiters_windows.go` and `exiters_plan9.go` files alongside the `core/` and `std/` directories.
The dependency list in `go.mod` explains a lot about the scope.
The `go.mod` file declares `go 1.27.0`, which is the toolchain floor for anyone building from source rather than downloading a release.
The stated purpose is scripting. The project goals say to be suitable for scripting, described as lightweight with fast startup, and call that something Clojure is not good at. Performance is listed under non-goals with a direct instruction: if you need it, use Clojure.
Installing through Homebrew, AUR, Nix, or a release binary
The README gives four routes and none of them require Go.
On macOS the shortest path is Homebrew:
brew install candid82/brew/jokerThe same command works on Linux if you use Homebrew on Linux. Arch Linux has an AUR package called `joker-bin`. Nix users run:
nix-env -i jokerFor anything else, or if you prefer manual installation, the README points at the precompiled binaries on the releases page and tells you to put the one for your platform on your PATH.
Building from source is also documented and linked as a section of the README. The repository root carries `build.sh`, `build-arm.sh` for ARM targets, and `run.sh`, so the build is a shell script rather than a documented `go build` recipe.
Recent releases are frequent enough to pin if you want to: v1.10.0 on 2026-08-23, v1.9.0 on 2026-07-02, and v1.8.3 on 2026-06-21. The last push was on 2026-10-05.
The project homepage is joker-lang.org, and a standard library reference is published at candid82.github.io/joker.
The REPL has parenthesis matching you may want to turn off
Running `joker` with no arguments launches the REPL. You exit it with `(exit)`, EOF such as Ctrl-D, or SIGINT such as Ctrl-C.
The editing behaviour is opinionated and the README says so. Typing an opening parenthesis in the REPL adds a matched pair, and the delete key removes individual parentheses ignoring the matching. Ctrl-D acts as a delete key on some systems, which is worth knowing before you conclude something is broken.
If that annoys you, the escape hatch is a flag, or rlwrap:
joker --no-readlineor, if you have rlwrap installed, `rlwrap joker --no-readline`.
Script execution has three forms. `joker foo.joke` runs a file, and `.joke` is the extension Joker uses. `joker -` reads a script from standard input. `joker -e '(println "Hello, world!")'` evaluates an expression directly.
There is one subtlety in the file form worth flagging because it is documented as a pitfall. A bare filename is normally a script, but `--exit-to-repl` before `--file <filename>` drops into the REPL after the script runs successfully. The README notes you must use `--file` there, to stop the filename being treated as a socket specification for the REPL.
Lint and format are the parts Clojure users actually install it for
The scripting interpreter is the smaller half of the appeal. The larger half is tooling that reads code without running the project it belongs to.
Linter mode runs on a single file:
joker --lint <filename>and on a whole tree with `--working-dir <dirname>`, which recursively lints all Clojure files in a directory. The project goals describe this as catching some basic errors, which is a deliberately modest claim.
Format mode writes the formatted source to standard output:
joker --format <filename>and `--format -` reads Clojure source from standard input, formats it and prints the result. Standard output as the destination matters for editor integrations and for diffing in review.
Format mode also handles EDN, and the README gives the exact invocation for pretty printing EDN read from stdin:
joker --hashmap-threshold -1 -e "(pprint (read))"The `--hashmap-threshold` parameter is documented in the v0.8.8 release description, which the README links for anyone curious about it. Setting it to -1 is what forces deterministic ordering instead of hash dependent ordering, and that ordering is the reason the flag exists.
An ecosystem follows from that. There is a Sublime Text plugin that uses Joker for pretty printing EDN files.
The task runner reads a tasks.joke file with no registration macro
`joker --task [<name>] [<args>...]` runs a task from a tasks file, defaulting to `tasks.joke` in the current directory. Tasks are simply public functions in the `tasks` namespace, and the README states that no special macro or registration is needed. Functions defined with `defn-` stay out of the list and can serve as helpers:
(ns tasks)
(defn greet
"Greet one or more people"
[& names]
(println (str "Hello, " (joker.string/join ", " names) "!")))
(defn default
"Run the default task"
[]
(greet "world"))`joker --task greet Alice Bob` calls greet with two string arguments. Arguments after the task name are passed straight to the function as strings, not parsed as Joker options. `joker --task` with no name calls the function named `default`.
Discovery uses `joker --list-tasks` or the shorter `--tasks`, which prints public tasks with the first line of each function's docstring. That is why the docstrings matter: they are the only description a user sees.
One collision is handled explicitly. `test` is already a core function in `joker.core`, so defining a `test` task warns about replacing it. Excluding it in the namespace declaration fixes it:
(ns tasks
(:refer-clojure :exclude [test]))
(defn test
"Run tests"
[]
(println "Running tests"))The README also notes that a top-level `(ns-unmap 'tasks 'test)` works at runtime but does not silence the linter, because the linter does not execute ordinary top-level function calls. So the exclusion has to be declarative.
EPL-1.0, an explicit non-goal list, and a dialect that stops short
Joker is licensed EPL-1.0, with the licence text in the LICENSE file at the repository root. That matters more here than it does for a typical Go utility, because Joker is used as a build and lint dependency in repositories that may otherwise be all Clojure. EPL-1.0 is a file level copyleft, so what it means in practice depends on whether you modify Joker's own files or only link to it. Read the licence rather than assuming MIT, because the labels are often conflated.
The README publishes project non-goals next to the goals, and that section is unusually direct. Performance is a non-goal: Joker executes bytecode by default, but high-performance execution is not the primary goal, and the instruction is to use Clojure if you need it.
On language fidelity there is a limit stated as clearly. The goal is to be as close, syntactically and semantically, to Clojure as possible, so that Joker should be a dialect of Clojure and not a language inspired by Clojure. The same paragraph then concedes there are a lot of Clojure features Joker does not and will never have, and that closeness applies only to the features it does have.
Two project documents are worth knowing about before you go further. `LIBRARIES.md` covers organising namespaces, and `VM.md` tracks VM implementation status, which is where you find out which parts of the runtime actually exist.
Where a ClojureScript clj-kondo setup, or clj itself, fits instead
Joker's linter can check Joker, Clojure and ClojureScript code and catches some basic errors. Joker's linter catches basic errors and needs no project classpath; that is exactly why it is fast and exactly why it will not tell you about an unresolved symbol in a namespace you have not loaded.
Where Joker genuinely has no substitute is the formatter as a binary you can drop into a pre-commit hook or a Docker image, combined with an interpreter that starts without a JVM. If your CI already runs on the JVM because you build the project anyway, the JVM tool already available is a reasonable choice and the binary argument disappears.
For running the code itself, the answer is in the non-goals: Clojure, not Joker.
So the honest split is by job. Read and check code with Joker, run it with Clojure.
Editorial conclusion
Adopt Joker if your job is reading and checking Clojure code from shell scripts, a pre-commit hook or CI, or if you want a Clojure-like REPL that starts without a JVM. Do not adopt it to run a production service: performance is a stated non-goal, and the author says plainly that there are Clojure features Joker does not and will never have. Verify first that your target Go toolchain matches the `go 1.27.0` directive in `go.mod` if you build from source with `build.sh`, and check which parts of your codebase the linter mode actually understands, since it is documented as catching some basic errors rather than analysing a full project.
Frequently asked questions
How do I install Joker?
On macOS and on Linux with Homebrew, run `brew install candid82/brew/joker`. Arch Linux has an AUR package named `joker-bin`, Nix users run `nix-env -i joker`, and other platforms can download a precompiled binary from the releases page and put it on PATH.
Can Joker run Clojure libraries?
Not all of them. The project goals say Joker should be a dialect of Clojure rather than a language inspired by it, while also stating that there are a lot of Clojure features it does not and will never have. LIBRARIES.md and VM.md cover what exists.
How do I turn off automatic parenthesis matching in the Joker REPL?
Run `joker --no-readline`, or `rlwrap joker --no-readline` if you have rlwrap installed, since the default REPL editing inserts a matched pair when you type an opening parenthesis.
How does the Joker task runner define tasks?
Tasks are public functions in the `tasks` namespace of a tasks.joke file, with no registration macro. `joker --task greet Alice Bob` calls a function with string arguments, `joker --task` calls `default`, and `joker --list-tasks` shows public tasks with their docstrings.
What licence is Joker released under?
EPL-1.0, with the text in the LICENSE file at the repository root.
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/candid82-joker)Star history chart for your README
[](https://hysenlabs.com/projects/candid82-joker)Was this useful?