Clojure compatibility matrix
What Clojure code runs on mino, what runs with small differences, and what is intentionally absent. The bar for this page is the Clojure dialect at embedded scale: code that does not reach for JVM interop or host-thread primitives runs on mino unchanged. Companion pages: intentional divergences spells out why mino diverges where it does, and Coming from Clojure gives a higher-level tour.
Coverage tracks the test suites under tests/clj_*_test.clj and tests/compat_test.clj. If a function or macro is listed as supported, the test suite exercises it.
Reading the table
- Supported - the function or macro behaves the way Clojure does for the inputs mino accepts.
- Differs - the name exists and is callable, but the behavior diverges deliberately. The note column explains the divergence and links to intentional divergences where appropriate.
- Absent - the name is not provided. Calling it raises a resolution error. The note explains the replacement (atoms instead of refs, protocols instead of
reify, etc.) or links the divergence.
Core language
| Form | Status | Note |
|---|---|---|
def / defn / defn- / defmacro | Supported | Including ^:private metadata and attribute maps. |
fn / let / letfn | Supported | Multi-arity, named, variadic; vector binding forms with destructuring. |
loop / recur | Supported | Tail position only inside loop and fn. |
if / when / if-not / when-not | Supported | |
if-let / when-let / if-some / when-some | Supported | |
cond / condp / case | Supported | case covers literal matching, multi-match lists, and a default clause. |
do / comment | Supported | |
and / or / not | Supported | |
quote / var / #'sym | Supported | |
Reader conditionals #? / #?@ | Supported | Active dialect keys are :mino and :clj (so :clj/:cljs-only libraries read on the :clj branch); :default is honored. read-string accepts an opts map with :read-cond :preserve to keep the form unevaluated. |
::keyword / ::alias/keyword | Supported | Auto-resolved keywords resolve at read time using the current namespace and alias table. |
Namespaced map literals #:foo{:b 1} / #::{:b 1} / #::alias{...} | Supported | Bare keys are qualified with the named, current, or aliased namespace; :_/x strips the prefix leaving a bare key. Duplicate keys after qualification error at read time. |
Destructuring
| Form | Status | Note |
|---|---|---|
| Vector positional destructuring | Supported | Including & rest and :as. |
Map :keys / :strs / :syms / :or / :as | Supported | Including nested destructuring. |
Namespaced map destructuring {:keys [::ns/x]} | Supported | Auto-resolved keywords resolve at read time, so namespaced :keys entries work. |
destructure function | Supported | Returns the flat [name init ...] vector for a binding pairs vector, ready to feed to let. |
Collections
| Form | Status | Note |
|---|---|---|
Persistent vector / hash-map / hash-set / list | Supported | Bagwell tries for vectors, HAMT for maps and sets, structural sharing throughout. |
sorted-map / sorted-set / sorted-map-by / sorted-set-by | Supported | Persistent left-leaning red-black tree; custom comparator via the -by variants. |
subseq / rsubseq | Supported | Bounded in-order walks against the rbtree. |
array-map | Differs | Alias for hash-map; HAMT is used at every size. |
conj / disj / assoc / dissoc / get / nth | Supported | |
into / merge / merge-with / zipmap | Supported | into accepts a transducer xform. |
update / update-in / assoc-in / get-in / select-keys / find | Supported | |
keys / vals / contains? | Supported | |
update-keys / update-vals | Supported | |
peek / pop / empty / rseq | Supported | |
Transients: transient / persistent! / assoc! / conj! / dissoc! / disj! / pop! | Supported | Vector / map / set transients. Sorted-map and sorted-set transients are not provided. |
defrecord / deftype / ->RecordName / map->RecordName | Supported | Real value types with field-slot storage, protocol dispatch, and instance?. (= record map-with-same-content) is false by design. |
byte-array / bytes? / bitstring? / aget on a bytes value / alength / #bytes "..." reader literal | Differs | byte-array returns an immutable MINO_BYTES value, not a host-mutable Java byte[]. aset on it throws :mino/state (the persistent-value model excludes in-place writes). Other Java primitive-array constructors (int-array, long-array, etc.) stay as MINO_HOST_ARRAY for backward compat. See Bytes and Bit Syntax. |
Bit syntax: bits / bits-get / subbits / let-bits | Extension | mino-only surface borrowed from Erlang's bit syntax. Builds and reads bit fields at arbitrary offsets / sizes / endians / types. JVM Clojure has no equivalent. |
Chunked-seq APIs (chunked-seq?, chunk-first, chunk-rest, chunk-next, chunk-cons, chunk-buffer, chunk-append, chunk) | Supported | Real MINO_CHUNKED_CONS value type with the canon API. map, filter, take, keep, keep-indexed, and map-indexed propagate chunkedness end-to-end. Sources auto-chunk since v0.98.3: (seq vec) emits 32-element chunks reusing the vector's leaves, and lazy range produces a fresh chunk on each force. |
Sequences
| Form | Status | Note |
|---|---|---|
lazy-seq / cons / seq / first / next / rest | Supported | |
(list) / '() | Supported | Returns the canonical empty list. (= '() nil) is false; (= '() []) is true. |
map / filter / remove / take / drop / take-while / drop-while | Supported | Multi-collection map works. |
range / iterate / repeat / cycle / iteration | Supported | |
concat / interleave / interpose / partition / partition-all / partition-by | Supported | |
reduce / reduce-kv / reductions / reduced / reduced? | Supported | |
Transducers: transduce / into w/ xform / sequence / eduction / completing / cat / halt-when / ensure-reduced | Supported | |
sort / sort-by / frequencies / group-by | Supported | |
some / every? / not-any? / not-every? | Supported | |
for / doseq | Supported | Multi-binding with :when, :while, :let. |
doall / dorun | Supported | |
min-key / max-key / random-sample / bounded-count / distinct? | Supported | |
clojure.core.reducers | Absent | Fork-join reducers are not provided. Transducers cover the throughput shape; future plus partitioning covers ad-hoc parallelism when threading is granted. |
Higher-order
| Form | Status | Note |
|---|---|---|
apply / partial / comp / complement | Supported | Variadic comp. |
juxt / every-pred / some-fn | Supported | |
Threading: -> / ->> / as-> / cond-> / cond->> / some-> / some->> / doto | Supported | |
Callable collections ({:a 1} :a) and keywords (:k m) | Supported | Including in higher-order contexts: (map :name coll), (filter #{:a} coll). |
Numbers
| Form | Status | Note |
|---|---|---|
64-bit integer (Long), 64-bit float (Double) | Supported | |
BigInt (1N literal, (bigint x)) | Supported | Backed by vendored MIT-licensed imath. MINO_BIGINT is a distinct type tag. |
Ratio (1/2 literal, (numerator r), (denominator r), (rationalize x)) | Supported | Always gcd-reduced; an exact integer ratio reduces to its MINO_INT or MINO_BIGINT value. |
BigDec (1.5M literal, (bigdec x), (with-precision n)) | Supported | Stored as {unscaled scale} over MINO_BIGINT. with-precision accepts every JVM rounding mode: :down, :up, :floor, :ceiling, :half-up, :half-down, :half-even (banker's), :unnecessary (throws when rounding would occur). |
Plain + / - / * / inc / dec | Differs | Auto-promotes to bigint on long overflow rather than raising ArithmeticException as JVM Clojure does for the unprimed forms. Equivalent to Clojure's +' / -' / *' / inc' / dec'. Use the unchecked-* family for wraparound. Documented under auto-promoting math. |
unchecked-+ / unchecked-- / unchecked-* / unchecked-inc / unchecked-dec / unchecked-divide-int | Supported | Fast int64 path with wraparound semantics, matching Clojure's unchecked-* family. |
rational? / ratio? / decimal? / integer? / bigint? / number? | Supported | Predicates point at the real numeric-tower types. rational? is true for int / bigint / ratio / bigdec; integer? is true for int and bigint; bigint? specifically tests the bigint tier. The narrow pos-int? / neg-int? / nat-int? predicates match only the long tier (per Clojure). |
mod / rem / quot | Supported | Preserve operand tier: bigint operands produce bigint results, ratio operands produce a bigint quot and a ratio rem / mod (collapsing to bigint when integer-valued), bigdec operands produce bigdec results at the aligned scale. |
< / <= / > / >= | Supported | Operands must be numeric ((< nil 1) throws); any ##NaN operand short-circuits the chain to false. |
bit-and / bit-or / bit-xor / bit-not / bit-shift-left / bit-shift-right | Supported | Long-only bit operations. |
Math/abs / Math/sqrt / Math/sin etc. | Absent | JVM static-call shape. Of the Java Math surface, only abs ships as a plain function in clojure.core today. Other transcendental functions (sqrt, sin, etc.) are not provided; the embedder can register them by wrapping <math.h> through the host capability registry. |
Characters and strings
| Form | Status | Note |
|---|---|---|
Character literals \A / \space / \uNNNN / \o77 | Supported | Distinct character type. (char? \A) is true; (string? \A) is false. |
char? / char / int | Supported | |
clojure.string: lower-case / upper-case / capitalize / reverse / blank? / starts-with? / ends-with? / includes? / escape / replace / replace-first / split / split-lines / join / trim / triml / trimr / trim-newline / index-of / last-index-of / re-quote-replacement | Supported | split accepts an optional 3rd limit argument matching canon String.split(re, limit): positive caps the result, zero or negative keeps trailing empties. |
Regex: re-find / re-matches / re-seq / re-pattern / re-matcher / re-groups | Supported | re-find and re-matches return [whole g1 g2 ...] for grouped patterns and the matched substring otherwise. re-matcher returns a stateful iterator that re-find advances; re-groups reads the last match. Reader literal #"..." bypasses string-escape processing so \d reaches the engine intact. |
Concurrency
| Form | Status | Note |
|---|---|---|
atom / swap! / reset! / compare-and-set! / deref / @ | Supported | |
clojure.core.async - chan / go / go-loop / <! / >! / <!! / >!! / alts! / alts!! / timeout / pipe / merge / into / reduce / transduce / split / partition-by / mult / tap / pub / sub / mix / pipeline / pipeline-async | Supported | Surface lives in the clojure.core.async namespace; merge, into, reduce, transduce, and partition-by shadow the clojure.core forms inside that ns. Cooperative scheduling for go blocks; <!! / >!! / alts!! park across OS threads when the host grants threading. Channels carry transducers and exception handlers. See host-grant-gated host threads for the embed contract. |
alt! macro | Absent | Use alts! (the function form). |
future / promise / deliver / thread / future-cancel / future-done? / future-cancelled? / realized? / future? | Same when host grants threads | Real OS-thread futures and promises backed by pthread_create (CreateThread on Windows). Embedded states default to thread_limit = 1 and throw :mino/unsupported until the host calls mino_set_thread_limit; standalone ./mino grants cpu_count by default, so the REPL surface matches canonical Clojure out of the box. See host-grant-gated host threads for the embed contract and the multi-tenant pool surface. |
pmap | Absent | Not provided. When threading is granted, the same shape is reachable as a small composition of future and deref over a partitioned input. |
ref / ref? | Supported | STM ref construction and identity predicate. See STM for the design choices and deviations. |
dosync / dosync* | Supported | Transaction body. dosync is a macro expanding to (dosync* (fn [] ...)). |
alter / commute / ref-set / ensure | Supported | In-tx writers. alter participates in read-set validation; commute does not and is replayed at commit. ref-set / alter after commute on the same ref throws "Can't set after commute" (JVM canon). |
io! | Supported | Throws :mino/state MST003 when called inside a transaction; outside, runs body unchanged. |
in-transaction? | Supported | Returns true inside a dosync body, false otherwise. |
add-watch / remove-watch on refs | Supported | Watches fire after commit. Same surface as on atoms and vars. |
add-watch / remove-watch on vars | Supported | Fire on alter-var-root (and def with rebind), not on binding thread-local pushes. Same callback signature as on atoms / refs. |
set-validator! / get-validator on vars | Supported | Validator runs before alter-var-root publishes the new root; throw rejects without mutating the var. |
set-validator! / get-validator on refs | Supported | Validators run during commit before any write becomes visible. Same surface as on atoms. |
ref-min-history / ref-max-history / ref-history-count | Stub | Returns 0 / 10 / 0 unconditionally. mino uses single-version optimistic locking and has no MVCC history to introspect. See STM. |
agent / agent? / send / send-off / await / await-for / agent-error / restart-agent / set-error-handler! / error-handler / set-error-mode! / error-mode | Supported | send enqueues onto the agent's POOLED run-queue, send-off onto SOLO; both return the agent immediately, and each pool's worker drains its queue under state_lock. await / await-for block until every named agent's in-flight count reaches zero. Each worker counts against thread_limit (the embedder thread does not; default 1 in embedded use, bumped to cpu_count in standalone ./mino); send throws MTH001 if the host hasn't granted a thread budget. Action throws and watch throws are both captured into agent-error. Public C-API mirrors: mino_send, mino_send_off, mino_await, mino_await_for, mino_agent_error, mino_restart_agent. See STM for the deviations. |
send-via | Absent | Intentionally deferred -- no public Executor type is exposed yet. send and send-off each have their own per-state worker (POOLED and SOLO). |
shutdown-agents | Supported | Quiesces both per-state workers (drains the queues, joins the pthreads) and seals the agent surface so subsequent send / send-off throw MST008. Idempotent. Throws MST002 if called from inside an action body. |
release-pending-sends | Supported | Inside a dosync, returns the count of queued sends and clears them so they don't fire on commit. Outside a transaction returns 0. |
add-watch / remove-watch on agents | Supported | Same callback signature as on atoms / refs / vars. Watch throws are captured into agent-error (one watch's throw does not silence later watches on the same publish). |
locking / monitor-enter / monitor-exit | Absent | Each runtime serializes mutator threads under a per-state lock; user code does not see preemption inside one runtime, so there is nothing to lock against from inside mino. |
volatile! / vswap! / vreset! / volatile? | Supported | Real MINO_VOLATILE value type with a single mutable slot. Stateful transducers (take, drop, drop-while, take-nth, interpose, distinct, partition-by, partition-all, map-indexed, dedupe) use volatiles for their per-step state. |
Multimethods, hierarchies, protocols
| Form | Status | Note |
|---|---|---|
defmulti / defmethod / remove-method / remove-all-methods / methods / get-method | Supported | Dispatch cache invalidates on hierarchy mutation. |
prefer-method / prefers | Supported | Transitive prefer through hierarchy parents matches Clojure semantics. |
defmulti :hierarchy option | Differs | mino dispatches against the global hierarchy only. See global hierarchy only. |
make-hierarchy / derive / underive / parents / ancestors / descendants / isa? | Supported | Both 2-arity (global) and 3-arity (explicit hierarchy) forms. |
defprotocol / extend-type / extend-protocol / satisfies? / extend / extends? | Supported | Per-method dispatch atoms, late-bound. |
definterface | Absent | Throws an informative error. Use defprotocol. |
reify / proxy | Absent | JVM-interop shapes. Use defprotocol + extend-type. See no reify / proxy. |
:extend-via-metadata on protocols | Differs | Not honored. Method-table extension only. |
Testing
| Form | Status | Note |
|---|---|---|
clojure.test/deftest / is / are / testing / run-tests | Supported | Standard test framework. (is (= 1 1)), (is (thrown? ...)), and (testing "description" ...) all work. |
use-fixtures / compose-fixtures / join-fixtures | Supported | Both :once and :each kinds. The use-fixtures form is a macro so the calling namespace is captured at expansion time -- mino's *ns* is the function's defining namespace, not a dynamic var. |
clojure.test.check - quick-check / generators / properties | Supported | Minimal port of the test.check API. Generators, properties, and quick-check ship; shrinking is deferred. Backs the s/gen and s/exercise hooks under clojure.spec.alpha. |
special-symbol? | Supported | Returns true for the Clojure-reserved special form names (fn, fn*, let, let*, loop, loop*, if, do, quote, def, var, set!, throw, try, catch, finally, recur, new, ., &, case*, deftype*, letfn*, and the mino-only ns, binding, lazy-seq, refer-clojure). Unimplemented JVM-only specials are reserved as a portability courtesy. |
Errors
| Form | Status | Note |
|---|---|---|
try / catch / finally / throw | Supported | throw accepts any value; catch always receives a structured diagnostic map. |
ex-info / ex-data / ex-message | Supported | |
with-open | Supported | |
assert | Supported |
Metadata and vars
| Form | Status | Note |
|---|---|---|
meta / with-meta / vary-meta / alter-meta! / reset-meta! | Supported | Including ^{:k v}, ^:k, ^Type reader syntax. |
var? / var-get / var-set / alter-var-root / with-redefs / with-redefs-fn / with-local-vars / intern / find-var / bound? | Supported | Vars are first-class: (def x 1) returns the var, (meta #'foo) returns {:ns ... :name ... :private ... :dynamic ...}, and (def x) creates an unbound var that bound? reports as false. |
bound-fn / bound-fn* / get-thread-bindings / with-bindings* | Supported | Capture and replay dynamic bindings around a thunk. |
I/O and system
| Form | Status | Note |
|---|---|---|
print / println / pr / prn / newline / pr-str / println-str | Supported | Routed through the print-method multimethod; user types extend printing with (defmethod print-method MyType ...). |
slurp / spit | Supported | |
read / read-string / clojure.edn/read / clojure.edn/read-string | Supported | Both accept an opts map with :read-cond (:allow default, :preserve, :disallow). clojure.edn/* forces :preserve so untrusted text never auto-evaluates a reader conditional. Stream-shaped java.io.PushbackReader arguments are not applicable; mino accepts strings only. |
tagged-literal / tagged-literal? / reader-conditional / reader-conditional? | Supported | Constructors and predicates for the reader-record shapes. User-defined tag dispatch via *data-readers* is honored; the *default-data-reader-fn* fallback applies for unknown tags. |
#inst "..." literal / inst? / inst-ms / clojure.instant | Supported | The #inst reader literal parses into a component map carrying {:mino/instant true} metadata. inst? detects the marker; inst-ms decodes epoch millis from the
component map. mino has no JVM Date / Timestamp class -- the map IS the representation. |
#uuid "..." literal / uuid? / random-uuid / parse-uuid | Supported | Real MINO_UUID value type. (str u) emits the canonical 36-char form so it round-trips
through java.util.UUID/fromString on JVM. |
*clojure-version* / (clojure-version) | Differs | Returns a four-key map and a "M.N.P" string respectively. The version numbers reflect mino,
not JVM Clojure -- the Clojure-shape is for code that
reads the map's structure; the literal version string
differs intentionally. |
Namespaces and host
| Form | Status | Note |
|---|---|---|
ns / require / :as / :as-alias / :refer / :use / :refer-clojure | Supported | Each namespace owns a root binding table; unqualified lookup walks lexical, then current-ns, then clojure.core parent. Modifiers: :only, :exclude, :rename, :refer :all (skips privates). Prefix lists work in :require. A loaded file's first (ns ...) must declare the requested module name. |
in-ns / find-ns / the-ns / create-ns / remove-ns / ns-name / ns-publics / ns-interns / ns-refers / ns-aliases / ns-map / ns-unmap / ns-unalias / alias / all-ns / loaded-libs / ns-resolve / requiring-resolve / *ns* | Supported | Full first-class namespace registry. *ns* is interned as a dynamic var that tracks user-visible namespace switches. Namespaces carry metadata (docstring, attribute map). Privacy is enforced on cross-namespace qualified access. |
Java interop (.method obj) / (.-field obj) / (Type/static) / (new Type ...) | Differs | Same syntax, but dispatched through the host capability registry. The host opts in to each method and getter. See the Embedding Guide. |
Class/forName / bean / proxy / gen-class / .. / set! | Absent | JVM reflection / interop surface. See no JVM interop. |
*warn-on-reflection* | Absent | There is no reflection in mino. |
Items marked supported round-trip through the test suite; an entry's existence here is a claim that tests/clj_*_test.clj or tests/compat_test.clj exercises it. If you find a divergence not documented above, file an issue at github.com/leifericf/mino/issues.