Blacktide is a Clojure-to-Clojure compiler library. Its loophole macro adds mutable lexical locals and explicit jumps within a bounded region of code. It compiles that region to ordinary Clojure, without mutable cells or an interpreter for local state.
(require '[com.tailrecursion.blacktide :refer [loophole setq psetq label goto]])
(loophole [i 0 total 0]
(label again)
(when (< i 5)
(setq total (+ total i) i (inc i))
(goto again))
total) ; => 10
(loophole [x 1 y 2]
(psetq x y y x)
[(setq x (+ x 10)) y]) ; => [12 1]
Both assignment forms work in expressions:
| Form | Assignment order | Result |
|---|---|---|
setq |
Evaluate and assign each pair in order. | Last value, or nil for no pairs. |
psetq |
Evaluate all right-hand sides, then assign all targets, each in pair order. | nil |
Labels belong to the region's outer statement sequence. Jumps can leave nested statement bodies, but every jump needs an explicit label.
The region supports a defined subset of Clojure:
let, destructuring, if, case, loop/recur, calls, collections and Java interop.when, cond, dotimes and doseq, provided their entire expansion uses supported forms.letfn, try, native set! and nested loophole. Define functions and manage resources outside the region.See the language reference for the supported forms and their semantics. The algorithm examples show partitioning and parsing. Blacktide makes no general speedup guarantee.
Requires Clojure 1.12.6 and JDK 17 or later. Clone the public repository:
git clone https://tailrecursion.com/repos/git/blacktide.git
To use that neighboring checkout, add this dependency to your project's deps.edn:
{:deps {com.tailrecursion/blacktide {:local/root "../blacktide"}}}
Clojure is the only dependency. Generated code needs no Blacktide runtime helpers. Typed Clojure checks compiler passes during development.
| Command | Purpose |
|---|---|
clojure -M:test |
Run semantic, generative, differential and AOT tests. |
clojure -M:check |
Check language contracts and production pass bodies. |
bin/check |
Run tests and type checks; accepts CLI -S options. |
clojure -T:build jar |
Build target/blacktide-0.1.0-SNAPSHOT.jar. |
Select a JDK with JAVA_CMD=/path/to/jdk/bin/java bin/check. Development targets JDK 25/27; 17/21 are compatibility targets. Forgejo CI tests all four JDKs, checks pass types on 25/27, and builds the JAR.
Generated tests use test.check, fixed seeds and independent models. Failures report the seed and shrunk input. Shrinking preserves lexical scope and iteration bounds. Focused regressions use ordinary clojure.test examples.
To narrow or reproduce a test run:
clojure -M:test for a targeted run.defspec function with a trial count and :seed, for example (generated-nested-loops 1000 :seed 161803).Copyright © 2026 Alan Dipert. Distributed under the MIT License.