Joker
Small Clojure interpreter, linter and formatter.
Install / Use
/learn @candid82/JokerREADME
Joker is a small Clojure interpreter, linter and formatter written in Go.
Installation
On macOS, the easiest way to install Joker is via Homebrew:
brew install candid82/brew/joker
The same command can be used on Linux if you use Homebrew on Linux.
If you use Arch Linux, there is AUR package.
If you use Nix, then you can install Joker with
nix-env -i joker
On other platforms (or if you prefer manual installation), download a precompiled binary for your platform and put it on your PATH.
You can also build Joker from the source code.
Usage
joker - launch REPL. Exit via (exit), EOF (such as Ctrl-D), or SIGINT (such as Ctrl-C).
Hint: In the REPL typing ( adds a pair of matched parentheses. Use the delete key to remove individual parenthesis ignoring parenthesis matching. Ctrl-D works as a delete key on some systems. If you find the default REPL editing behavior annoying (e.g., automatic parenthesis matching, backspace doesn't delete individual parenthesis), try joker --no-readline or rlwrap joker --no-readline if you have rlwrap installed.
joker <filename> - execute a script. Joker uses .joke filename extension. For example: joker foo.joke. Normally exits after executing the script, unless --exit-to-repl is specified before --file <filename>
in which case drops into the REPL after the script is (successfully) executed. (Note use of --file in this case, to ensure <filename> is not treated as a <socket> specification for the repl.)
joker --eval <expression> - execute an expression. For example: joker -e '(println "Hello, world!")'. Normally exits after executing the script, unless --exit-to-repl is specified before --eval,
in which case drops into the REPL after the expression is (successfully) executed.
joker - - execute a script on standard input (os.Stdin).
joker --lint <filename> - lint a source file. See Linter mode for more details.
joker --lint --working-dir <dirname> - recursively lint all Clojure files in a directory.
joker --format <filename> - format a source file and write the result to standard output. See Format mode for more details.
joker --format - - read Clojure source code from standard input, format it and print the result to standard output.
Documentation
Dash docset: dash-feed://https%3A%2F%2Fraw.githubusercontent.com%2Fcandid82%2Fjoker%2Fmaster%2Fdocs%2Fjoker.xml
(either copy and paste this link to your browser's url bar or open it in a terminal with open command)
Organizing libraries (namespaces)
Project goals
These are high level goals of the project that guide design and implementation decisions.
- Be suitable for scripting (lightweight, fast startup). This is something that Clojure is not good at and my personal itch I am trying to scratch.
- Be user friendly. Good error messages and stack traces are absolutely critical for programmer's happiness and productivity.
- Provide some tooling for Clojure and its dialects. Joker has linter mode which can be used for linting Joker, Clojure and ClojureScript code. It catches some basic errors. Joker can also format (pretty print) Clojure code (see format mode) or EDN data structures. For example, the following command can be used to pretty print EDN data structure (read from stdin):
joker --hashmap-threshold -1 -e "(pprint (read))"
There is Sublime Text plugin that uses Joker for pretty printing EDN files. Here you can find the description of --hashmap-threshold parameter, if curious.
- Be as close (syntactically and semantically) to Clojure as possible. Joker should truly be a dialect of Clojure, not a language inspired by Clojure. That said, there is a lot of Clojure features that Joker doesn't and will never have. Being close to Clojure only applies to features that Joker does have.
Project Non-goals
- Performance. If you need it, use Clojure. Joker is a naive implementation of an interpreter that evaluates unoptimized AST directly. I may be interested in doing some basic optimizations but this is definitely not a priority.
- Have all Clojure features. Some features are impossible to implement due to a different host language (Go vs Java), others I don't find that important for the use cases I have in mind for Joker. But generally Clojure is a pretty large language at this point and it is simply unfeasible to reach feature parity with it, even with naive implementation.
Differences with Clojure
- Primitive types are different due to a different host language and desire to simplify things. Scripting doesn't normally require all the integer and float types, for example. Here is a list of Joker's primitive types:
| Joker type | Corresponding Go type | | ---------- | --------------------- | | BigFloat | big.Float (see below) | | BigInt | big.Int | | Boolean | bool | | Char | rune | | Double | float64 | | Int | int | | Keyword | n/a | | Nil | n/a | | Ratio | big.Rat | | Regex | regexp.Regexp | | String | string | | Symbol | n/a | | Time | time.Time |
See Floating-point Constants and the BigFloat Type for more on BigFloat (M-suffixed) constants.
Note that Nil is a type that has one value: nil.
- The set of persistent data structures is much smaller:
| Joker type | Corresponding Clojure type | | ---------- | --------------------------------------------------------------------------------------------------------- | | ArrayMap | PersistentArrayMap | | MapSet | PersistentHashSet (or hypothetical PersistentArraySet, depending on which kind of underlying map is used) | | HashMap | PersistentHashMap | | List | PersistentList | | Vector | PersistentVector |
- Joker doesn't have the same level of interoperability with the host language (Go) as Clojure does with Java or ClojureScript does with JavaScript. It doesn't have access to arbitrary Go types and functions. There is only a small fixed set of built-in types and interfaces. Dot notation for calling methods is not supported (as there are no methods). All Java/JVM specific functionality of Clojure is not implemented for obvious reasons.
- Joker is single-threaded with no support for parallelism. Therefore no refs, agents, futures, promises, locks, volatiles, transactions,
p*functions that use multiple threads. Vars always have just one "root" binding. Joker does have core.async style support for concurrency. Seegomacro documentation for details. - The following features are not implemented: protocols, records, structmaps, chunked seqs, transients, tagged literals, unchecked arithmetics, primitive arrays, custom data readers, transducers, validators and watch functions for vars and atoms, hierarchies, sorted maps and sets.
- Unrelated to the features listed above, the following function from clojure.core namespace are not currently implemented but will probably be implemented in some form in the future:
subseq,iterator-seq,reduced?,reduced,mix-collection-hash,definline,re-groups,hash-ordered-coll,enumeration-seq,compare-and-set!,rationalize,load-reader,find-keyword,comparator,resultset-seq,file-seq,sorted?,ensure-reduced,rsubseq,pr-on,seque,alter-var-root,hash-unordered-coll,re-matcher,unreduced. - Built-in namespaces have
jokerprefix. The core namespace is calledjoker.core. Other built-in namespaces includejoker.string,joker.json,joker.os,joker.base64etc. See standard library reference for details. - Joker doesn't support AOT compilation and
(-main)entry point as Clojure does. It simply reads s-expressions from the file and executes them sequentially. If you want some code to be executed only if the file it's in is passed asjokerargument but not if it's loaded from other files, use(when (= *main-file* *file*) ...)idiom. See https://github.com/candid82/joker/issues/277 for details. - Miscellaneous:
caseis just a syntactic sugar on top ofcondpand doesn't require options to be constants. It scans all the options sequentially.slurponly takes one argument - a filename (string) or a reader object (e.g.*in*). No options are supported.ifn?is calledcallable?- Map entry is represented as a two-element vector.
- resolving unbound var returns
nil, not the valueUnbound. You can still check if the var is bound withbound?function.
Linter mode
Related Skills
node-connect
337.4kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
xurl
337.4kA CLI tool for making authenticated requests to the X (Twitter) API. Use this skill when you need to post tweets, reply, quote, search, read posts, manage followers, send DMs, upload media, or interact with any X API v2 endpoint.
frontend-design
83.2kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
337.4kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
