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

Core language

FormStatusNote
def / defn / defn- / defmacroSupportedIncluding ^:private metadata and attribute maps.
fn / let / letfnSupportedMulti-arity, named, variadic; vector binding forms with destructuring.
loop / recurSupportedTail position only inside loop and fn.
if / when / if-not / when-notSupported
if-let / when-let / if-some / when-someSupported
cond / condp / caseSupportedcase covers literal matching, multi-match lists, and a default clause.
do / commentSupported
and / or / notSupported
quote / var / #'symSupported
Reader conditionals #? / #?@SupportedActive 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/keywordSupportedAuto-resolved keywords resolve at read time using the current namespace and alias table.
Namespaced map literals #:foo{:b 1} / #::{:b 1} / #::alias{...}SupportedBare 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

FormStatusNote
Vector positional destructuringSupportedIncluding & rest and :as.
Map :keys / :strs / :syms / :or / :asSupportedIncluding nested destructuring.
Namespaced map destructuring {:keys [::ns/x]}SupportedAuto-resolved keywords resolve at read time, so namespaced :keys entries work.
destructure functionSupportedReturns the flat [name init ...] vector for a binding pairs vector, ready to feed to let.

Collections

FormStatusNote
Persistent vector / hash-map / hash-set / listSupportedBagwell tries for vectors, HAMT for maps and sets, structural sharing throughout.
sorted-map / sorted-set / sorted-map-by / sorted-set-bySupportedPersistent left-leaning red-black tree; custom comparator via the -by variants.
subseq / rsubseqSupportedBounded in-order walks against the rbtree.
array-mapDiffersAlias for hash-map; HAMT is used at every size.
conj / disj / assoc / dissoc / get / nthSupported
into / merge / merge-with / zipmapSupportedinto accepts a transducer xform.
update / update-in / assoc-in / get-in / select-keys / findSupported
keys / vals / contains?Supported
update-keys / update-valsSupported
peek / pop / empty / rseqSupported
Transients: transient / persistent! / assoc! / conj! / dissoc! / disj! / pop!SupportedVector / map / set transients. Sorted-map and sorted-set transients are not provided.
defrecord / deftype / ->RecordName / map->RecordNameSupportedReal 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 literalDiffersbyte-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-bitsExtensionmino-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)SupportedReal 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

FormStatusNote
lazy-seq / cons / seq / first / next / restSupported
(list) / '()SupportedReturns the canonical empty list. (= '() nil) is false; (= '() []) is true.
map / filter / remove / take / drop / take-while / drop-whileSupportedMulti-collection map works.
range / iterate / repeat / cycle / iterationSupported
concat / interleave / interpose / partition / partition-all / partition-bySupported
reduce / reduce-kv / reductions / reduced / reduced?Supported
Transducers: transduce / into w/ xform / sequence / eduction / completing / cat / halt-when / ensure-reducedSupported
sort / sort-by / frequencies / group-bySupported
some / every? / not-any? / not-every?Supported
for / doseqSupportedMulti-binding with :when, :while, :let.
doall / dorunSupported
min-key / max-key / random-sample / bounded-count / distinct?Supported
clojure.core.reducersAbsentFork-join reducers are not provided. Transducers cover the throughput shape; future plus partitioning covers ad-hoc parallelism when threading is granted.

Higher-order

FormStatusNote
apply / partial / comp / complementSupportedVariadic comp.
juxt / every-pred / some-fnSupported
Threading: -> / ->> / as-> / cond-> / cond->> / some-> / some->> / dotoSupported
Callable collections ({:a 1} :a) and keywords (:k m)SupportedIncluding in higher-order contexts: (map :name coll), (filter #{:a} coll).

Numbers

FormStatusNote
64-bit integer (Long), 64-bit float (Double)Supported
BigInt (1N literal, (bigint x))SupportedBacked by vendored MIT-licensed imath. MINO_BIGINT is a distinct type tag.
Ratio (1/2 literal, (numerator r), (denominator r), (rationalize x))SupportedAlways gcd-reduced; an exact integer ratio reduces to its MINO_INT or MINO_BIGINT value.
BigDec (1.5M literal, (bigdec x), (with-precision n))SupportedStored 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 / decDiffersAuto-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-intSupportedFast int64 path with wraparound semantics, matching Clojure's unchecked-* family.
rational? / ratio? / decimal? / integer? / bigint? / number?SupportedPredicates 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 / quotSupportedPreserve 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.
< / <= / > / >=SupportedOperands 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-rightSupportedLong-only bit operations.
Math/abs / Math/sqrt / Math/sin etc.AbsentJVM 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

FormStatusNote
Character literals \A / \space / \uNNNN / \o77SupportedDistinct character type. (char? \A) is true; (string? \A) is false.
char? / char / intSupported
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-replacementSupportedsplit 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-groupsSupportedre-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

FormStatusNote
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-asyncSupportedSurface 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! macroAbsentUse alts! (the function form).
future / promise / deliver / thread / future-cancel / future-done? / future-cancelled? / realized? / future?Same when host grants threadsReal 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.
pmapAbsentNot provided. When threading is granted, the same shape is reachable as a small composition of future and deref over a partitioned input.
ref / ref?SupportedSTM ref construction and identity predicate. See STM for the design choices and deviations.
dosync / dosync*SupportedTransaction body. dosync is a macro expanding to (dosync* (fn [] ...)).
alter / commute / ref-set / ensureSupportedIn-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!SupportedThrows :mino/state MST003 when called inside a transaction; outside, runs body unchanged.
in-transaction?SupportedReturns true inside a dosync body, false otherwise.
add-watch / remove-watch on refsSupportedWatches fire after commit. Same surface as on atoms and vars.
add-watch / remove-watch on varsSupportedFire 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 varsSupportedValidator runs before alter-var-root publishes the new root; throw rejects without mutating the var.
set-validator! / get-validator on refsSupportedValidators run during commit before any write becomes visible. Same surface as on atoms.
ref-min-history / ref-max-history / ref-history-countStubReturns 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-modeSupportedsend 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-viaAbsentIntentionally deferred -- no public Executor type is exposed yet. send and send-off each have their own per-state worker (POOLED and SOLO).
shutdown-agentsSupportedQuiesces 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-sendsSupportedInside 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 agentsSupportedSame 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-exitAbsentEach 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?SupportedReal 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

FormStatusNote
defmulti / defmethod / remove-method / remove-all-methods / methods / get-methodSupportedDispatch cache invalidates on hierarchy mutation.
prefer-method / prefersSupportedTransitive prefer through hierarchy parents matches Clojure semantics.
defmulti :hierarchy optionDiffersmino dispatches against the global hierarchy only. See global hierarchy only.
make-hierarchy / derive / underive / parents / ancestors / descendants / isa?SupportedBoth 2-arity (global) and 3-arity (explicit hierarchy) forms.
defprotocol / extend-type / extend-protocol / satisfies? / extend / extends?SupportedPer-method dispatch atoms, late-bound.
definterfaceAbsentThrows an informative error. Use defprotocol.
reify / proxyAbsentJVM-interop shapes. Use defprotocol + extend-type. See no reify / proxy.
:extend-via-metadata on protocolsDiffersNot honored. Method-table extension only.

Testing

FormStatusNote
clojure.test/deftest / is / are / testing / run-testsSupportedStandard test framework. (is (= 1 1)), (is (thrown? ...)), and (testing "description" ...) all work.
use-fixtures / compose-fixtures / join-fixturesSupportedBoth :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 / propertiesSupportedMinimal 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?SupportedReturns 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

FormStatusNote
try / catch / finally / throwSupportedthrow accepts any value; catch always receives a structured diagnostic map.
ex-info / ex-data / ex-messageSupported
with-openSupported
assertSupported

Metadata and vars

FormStatusNote
meta / with-meta / vary-meta / alter-meta! / reset-meta!SupportedIncluding ^{: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?SupportedVars 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*SupportedCapture and replay dynamic bindings around a thunk.

I/O and system

FormStatusNote
print / println / pr / prn / newline / pr-str / println-strSupportedRouted through the print-method multimethod; user types extend printing with (defmethod print-method MyType ...).
slurp / spitSupported
read / read-string / clojure.edn/read / clojure.edn/read-stringSupportedBoth 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?SupportedConstructors 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.instantSupportedThe #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-uuidSupportedReal 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)DiffersReturns 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

FormStatusNote
ns / require / :as / :as-alias / :refer / :use / :refer-clojureSupportedEach 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*SupportedFull 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 ...)DiffersSame 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!AbsentJVM reflection / interop surface. See no JVM interop.
*warn-on-reflection*AbsentThere 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.