See the README for installation and a quick start, and the API reference for public API contracts.
Fallback adds no minimum JDK beyond the supported Clojure runtime. On a
JVM without supported value-class capability, defvaluetype uses ordinary
identity deftype semantics, with generated constructor checks for enabled
non-nil fields. See the field contract.
Fallback does not emulate value equality or identity. Libraries
that allow fallback must work with both representations.
Set the policy with the JVM option:
clojure -J-Dvalue-type.fallback=error -M -m your.application
value-type.fallback | Behavior when fallback is needed |
|---|---|
silent (default) | Use deftype without a diagnostic. |
warn | Warn on standard error once per loaded library runtime. |
error | Reject every attempt with java.lang.Error. |
The policy applies if it is set before the first read: the first fallback
decision or the first value-type.capability/report call. Loading the library
does not read the policy. A namespace that evaluates a fallback definition at
top level makes the first read when it loads. Examples: a source definition on
a runtime without value-class support, or an AOT-compiled fallback definition
on any runtime. Thus the JVM option stays the recommended form. A
System/setProperty call, for example in -main, applies only if no policy
read occurred before it. report shows the policy in effect in
:fallback-policy; call it to check the policy.
The first read fixes the policy for the loaded library runtime. An invalid value fails at the first read, not at load, and each later read throws the same exception. A property change after the first read has no effect. There are no per-type overrides.
All policies use value classes when supported. Requiring the library alone does not warn. Concurrent attempts across types and namespaces share one warning flag; independent library runtimes have separate flags.
Selecting fallback during macroexpand or macroexpand-1 applies policy,
even if the expansion is discarded. Returned expansions retain identity
representation and enforce the receiving runtime's policy when compiled
and evaluated. Catching a strict failure does not permit a later attempt.
Source compiler operations wrap the Error in Compiler$CompilerException;
inspect its cause. A direct runtime policy rejection throws plain Error.
Invalid forms and loader or transform defects remain failures, not fallback.
Both representations reject mutable fields.
Set value-type.value-classes=off to go back to identity semantics after a
JVM value-class defect, with no code change and with preview kept on:
clojure -J-Dvalue-type.value-classes=off -M -m your.application
value-type.value-classes | Behavior |
|---|---|
auto (default) | Use value classes when the runtime supports them. |
off | Do not use value classes. Definitions use fallback. |
With off, report shows :disabled for :value-class,
:native-non-nil-field and :array-allocation, on every JVM.
:array-inspection does not change. Definitions use the fallback policy:
under error each definition throws, so use off with silent or warn.
make-array and into-array throw :array-unavailable with
:reason :disabled.
The library reads the property once, with the value-class capability, at the
first definition or the first report, make-array or into-array call. A
namespace with a top-level definition makes the first read when it loads.
Thus the JVM option is the recommended form. A System/setProperty call
applies only if no read occurred before it.
Saved value output (AOT) reads the property where the definition form runs.
With off, it throws :value-capability-unavailable with :reason :disabled, with or without native non-nil fields. It does not convert to
identity classes. See AOT limits.
AOT compilation fixes representation. A fallback artifact stays an identity
class on a capable JVM. The guard applies the receiving application's policy
where the fallback form is evaluated: warn once or reject under error.
A top-level form enforces policy during namespace initialization. A form
inside a function permits namespace loading, even under error, and enforces
policy when that function runs. Repeated strict calls each throw plain Error.
After strict initialization failure, a retry can throw NoClassDefFoundError
rather than run the guard again; this is also a Java Error. Guards cannot
prevent all prior JVM class loading and do not cover direct class access
that bypasses evaluation of the guarded form.
Value-class AOT output uses the compiling JDK's feature version and preview
minor version. It requires a compatible preview-enabled JVM; it does not
convert to identity classes on another JVM. Each saved value definition
checks the value-class capability where its form is evaluated. Where the
capability is unavailable (for example value-type.value-classes=off or a
failed self-check), it throws :value-capability-unavailable with the
reason. A top-level form throws at namespace initialization: require
throws ExceptionInInitializerError with the ex-info as cause, and a
retry can throw NoClassDefFoundError. A form inside a function permits
namespace loading and throws the ex-info at each invocation. The check
does not stop JVM class loading; a JVM that rejects the class file can give
its class-loading error first.
deftypeIn value mode, objects have no identity. These differences come from the JVM. They do not apply to ordinary identity fallback:
identical? (the Java == operator, acmp) compares field values.equals and hashCode use field values.double field compares by bit pattern in identical?. So NaN
equals NaN, and 0.0 does not equal -0.0.Long under
--enable-preview, or a defvaluetype instance) compares by its
field values.locking, WeakReference and other identity operations throw
IdentityException.Other differences:
:volatile-mutable or
:unsynchronized-mutable metadata gives an error.ex-info with the key :value-type/error and
:class-name. The defvaluetype Errors list gives
the data keys of each error:
:invalid-field: a field declaration is not valid (at macroexpansion),
or a field tag resolves to a different Class in the compilation loader
(:reason :field-class-mismatch, in the loader).:mutable-field: a field is mutable (at macroexpansion).:not-installed: a value definition, or an identity definition with
enabled fields or the closed shape, was fully expanded outside
compilation, for example with clojure.walk/macroexpand-all (at
macroexpansion). One-step macroexpand does not throw. Evaluate the
defvaluetype form directly.:not-value-class: the class is an identity class (at run time). Known
cause: a defvaluetype is inside a method body of another
defvaluetype. Move the inner form out of the method body to the top
level.:value-capability-unavailable: value output reaches a JVM where the
value-class capability is unavailable: a transferred value expansion
(receiving boundary, at analysis), or saved AOT output (where the form
is evaluated, see AOT limits). :reason gives the
capability reason, for example :disabled.:field-capability-unavailable: output with fixed :native modes
reaches a JVM with value-class capability but without native field
support: a transferred expansion at analysis, or saved AOT output at
namespace initialization or function invocation. There is no fallback
or enforcement reselection. The value-class guard runs first; if value
capability is unavailable, it throws :value-capability-unavailable.:invalid-field-enforcement: transferred enforcement modes are not valid,
or identity fields have modes that are not :generated (receiving boundary).:jvm-rejected: the JVM rejected a generated user class, for value or
identity output. This is not retried as fallback.:unexpected-constructor: for value output, the Clojure compiler output
has an unknown constructor shape.:unsupported-shape: for identity output, the Clojure compiler output
has an unknown class or constructor shape.deftype syntax error gives the error of deftype.:invalid-field, :mutable-field and the
macroexpansion :not-installed are the cause of a CompilerException
(macroexpansion error). The receiving-boundary errors
(:value-capability-unavailable, :field-capability-unavailable and
:invalid-field-enforcement) are the cause of a CompilerException
(the transferred expansion is macroexpanded in analysis). In saved AOT
output, :value-capability-unavailable and :field-capability-unavailable
come at namespace initialization (cause of ExceptionInInitializerError)
or directly at function invocation. The loader errors
(:invalid-field :field-class-mismatch, :jvm-rejected,
:unexpected-constructor and :unsupported-shape) are the cause of a
CompilerException (the class is defined in analysis). The run-time
:not-value-class comes directly from eval, and is
the cause of a CompilerException from load, require and
load-string. Use ex-cause to get the ex-info from the
CompilerException. A (catch clojure.lang.ExceptionInfo ...) does not
catch a CompilerException.Every value expansion, and each identity expansion with enabled non-nil
fields, retains a receiving boundary that installs a receiver-local loader
(macroexpand and macroexpand-1). Their expansions
support separate eval (including (eval (macroexpand form))),
metadata-preserving source loading and function analysis followed by invocation.
Receiving analysis resolves local field Classes but retains the selected
representation and enforcement modes. Incompatible receivers reject fixed value
output rather than select identity output. Fixed identity output remains subject
to the receiving fallback policy, even on a value-capable JVM.
Preserve field metadata when serializing expansions.
value-type.array/make-array allocates a one-dimensional native,
null-restricted atomic array. For this experimental API, select the Valhalla
build in the JVM facts. See the
runtime guide for provisioning and support limits.
Start with both flags:
export JAVA_CMD=<valhalla-jdk-home>/bin/java
clojure -J--enable-preview \
-J--add-exports=java.base/jdk.internal.value=ALL-UNNAMED -M
(require '[value-type.core :refer [defvaluetype]]
'[value-type.array :as va])
(defvaluetype ArrayPoint [^long x])
(def points (va/make-array ArrayPoint 3 (ArrayPoint. 0)))
(alength points) ;=> 3
(.-x (aget points 2)) ;=> 0
(aset points 1 (ArrayPoint. 7))
(.-x (aget points 1)) ;=> 7
Every slot starts with the supplied initial value. The component must be a
non-primitive runtime value class, not an identity class. It need not come
from defvaluetype, but the JVM makes the final allocation decision.
Length must be an integer from zero through Integer/MAX_VALUE. A valid
length does not guarantee sufficient memory. The initial value must be
non-null and compatible with the component, even when length is zero.
The result works with ordinary Clojure and Java array operations. Later
null stores throw NullPointerException; incompatible stores throw
ArrayStoreException. These are native exceptions, not library validation
errors. The caller owns the mutable slots after return; the library retains
no array. Atomic element access does not provide safe publication,
synchronization or atomic compound updates.
Validation stops at the first error in this order. Each library validation
error is ex-info with a :value-type/error key:
| Order | Key | Meaning |
|---|---|---|
| 1 | :array-unavailable | Required JVM capability, API or access is absent. |
| 2 | :invalid-array-component | The component is not a non-primitive value class. |
| 3 | :invalid-array-length | The length is not an integer in the accepted range. |
| 4 | :invalid-array-element | The initial value is null or incompatible. |
Error data includes :reason and relevant :component and :length
context. Available underlying causes are retained. Native allocation
rejection, unexpected failures and JVM errors propagate; they do not mean
that capability is absent.
Requiring value-type.array is safe on Java 8 and on Valhalla without
preview or exports. Allocation capability is checked lazily and cached per
loaded library runtime; startup configuration is fixed. Public require alone
does not load either optional array adapter. After the prerequisites pass,
first allocation discovery loads and compiles its source adapter and runs the
allocation self-check. Successful discovery caches direct-call functions.
Unexpected adapter loading, compilation or linkage failures propagate; they
are not capability absence. Unsupported calls
report :array-unavailable, even if their inputs are also invalid.
There is no ordinary-array fallback or warning policy. Array allocation is
independent of value-type.fallback; that property controls ordinary fallback
through defvaluetype, including definitions with non-nil fields.
Flattening is not guaranteed. A non-flat restricted atomic array is a valid result, including with array flattening disabled. Support for other JVM builds or untested value classes is not implied. No clone, copy or performance guarantee is made.
value-type.array/into-array builds the same native null-restricted atomic
arrays from finite seqable input. Use the JVM flags and ArrayPoint definition
above. Both forms preserve input order and exact length:
(def inferred (va/into-array ArrayPoint [(ArrayPoint. 2) (ArrayPoint. 5)]))
(mapv #(.-x %) inferred) ;=> [2 5]
(def explicit (va/into-array ArrayPoint (ArrayPoint. 99)
(list (ArrayPoint. 2) (ArrayPoint. 5))))
(mapv #(.-x %) explicit) ;=> [2 5]
(alength (va/into-array ArrayPoint (ArrayPoint. 0) [])) ;=> 0
(alength (va/into-array ArrayPoint (ArrayPoint. 0) nil)) ;=> 0
The two-argument form uses the first element as its allocation seed. Empty
or nil input throws :empty-array-input; use the explicit-seed form instead.
The three-argument form requires a compatible non-null seed even for empty
input. The seed adds no output slot and need not equal the first input value.
A null field inside a valid value object is not a null array element.
Validation checks allocation capability, then the component, then the explicit
seed (if supplied), before consuming input. These checks use the allocation
errors above. Explicit-seed errors have reason :null-initial or
:incompatible-initial, with no element index or invented length. Unsupported
calls report :array-unavailable without consuming input; there is no fallback.
During traversal, each proposed length is checked before the element is
validated and stored. Null or incompatible entries throw ex-info with
:value-type/error :invalid-array-element, :component, a zero-based :index
and reason :null-element or :incompatible-element. This includes the first
entry in the inferred-seed form. Error data does not retain the input or the
rejected element. Non-seqable input uses ordinary Clojure input errors.
The constructor traverses the original input once into temporary storage,
then allocates and copies the buffered values. This costs O(n) temporary
references in addition to the result array; it is not streaming or zero-copy.
Infinite input is unsupported. Length cannot exceed Integer/MAX_VALUE;
memory can run out before that limit. No speed or allocation benefit is claimed.
Input realization failures propagate unchanged. Failure does not undo input side effects and does not promise to consume the rest of the input. Chunked lazy producers can realize ahead; one traversal does not mean exactly one callback per requested element. Only a complete array is returned. Temporary storage and unfinished arrays are not retained in library state. Returned arrays have the ownership, later-store and concurrency limits described above; flattening is not required or guaranteed.
value-type.array/array-info inspects an existing reference array. It accepts
ordinary Object/String arrays, typed value arrays and restricted arrays from
make-array. Empty arrays and arrays whose components are arrays are valid.
It returns a complete map with these keys:
| Key | Value |
|---|---|
:component-class | The runtime component Class. |
:component-value? | boolean: the component is a value class. |
:null-restricted? | boolean: the JVM reports null restriction. |
:atomic? | boolean: the JVM reports atomic element access. |
:flat? | boolean: the JVM reports flattened storage. |
(va/array-info (object-array 0))
;=> {:component-class java.lang.Object, :component-value? false,
; :null-restricted? false, :atomic? true, :flat? false}
(va/array-info points) ; Inspect the restricted array from the example above.
Historical example result, verified on Valhalla 27-jep401ea3+1-1 with Clojure
1.12.6. Inspection requires the export flag shown above. Ordinary reference
arrays do not require --enable-preview for inspection on that build.
Creating value classes and allocating restricted arrays still require preview.
Inspection capability is separate from allocation capability and class fallback.
It is checked lazily and cached per loaded runtime; set flags before startup.
After API and access checks pass, first inspection discovery loads and compiles
its own source adapter, independently of allocation. Successful discovery
caches its direct-call function. Unexpected loading, compilation or linkage
failures propagate.
Validation precedes capability checks, unlike allocation. Null, non-array and
primitive-array inputs throw ExceptionInfo with
:value-type/error :invalid-array, even on unsupported JVMs. For a valid
reference array, missing API support or access throws ExceptionInfo with
:value-type/error :array-inspection-unavailable. Available causes are retained.
Unexpected failures propagate. The function returns all five properties or
fails; it does not use false for unknown facts.
Inspection does not change or retain the caller's array. Properties come from
the JVM, not from a class name or successful allocation. A value component does
not prove flattening or null restriction. A false :flat? is valid, including
with flattening disabled. Atomic access does not provide compound-update safety,
safe publication or caller synchronization.
This is an experimental API, not a general object inspector or a layout-size estimate. Support for other JVM builds is not implied. Use the public wrapper: native diagnostic calls with null can crash the tested JVM. See the runtime guide for verification commands.
value-type.capability/report returns the optional JVM capabilities and the
fallback policy in effect of the loaded runtime as an EDN map. Use it to check
a deployment before you define types or allocate arrays.
(require '[value-type.capability :as capability])
(capability/report)
;=> {:fallback-policy :silent,
; :value-class {:available? true},
; :native-non-nil-field {:available? true},
; :array-allocation {:available? false, :reason :array-access-denied},
; :array-inspection {:available? false, :reason :array-access-denied}}
Historical example result from Valhalla 27-jep401ea3+1-1 with
--enable-preview and without the export flag.
| Key | Meaning |
|---|---|
:value-class | defvaluetype can define value classes. |
:native-non-nil-field | Eligible :non-nil fields use native restriction. |
:array-allocation | make-array and into-array can allocate. |
:array-inspection | array-info can inspect. |
:fallback-policy | The fallback policy in effect: :silent, :warn or :error. |
Each capability value is {:available? true} or
{:available? false :reason r}. The reason keywords are in the
API reference. Each value equals the :available? and :reason
entries of the cached result that definitions and array functions use.
report runs the probes that have not run yet. A probe defect propagates; no
partial report is returned. Successful first array discovery through report
also loads and compiles the relevant optional source adapters; requiring the
public namespaces alone does not. The measured comparison
found about 3–4 ms additional first-use time for this work. This is a bounded
measurement, not a general speed guarantee.
The value-class, native field and array allocation probes check real
instances before they report a capability. The array inspection probe checks
the JDK API and access; its optional adapter must also load and compile
successfully. Unexpected compilation or linkage failures propagate, rather
than produce :available? false. The probe reasons below are classified
absences. Their effect depends on the key:
:value-class: a probe class that is not a value class gives
:probe-not-value-class. A failed semantic self-check gives
:failed-self-check. Definitions use fallback, as for other reasons.:native-non-nil-field: a JVM that accepts nil in a NullRestricted
probe field gives :native-restriction-ignored. New value definitions use
generated enforcement for eligible :non-nil fields. If value-class
capability is available, output with fixed :native modes instead fails
with :field-capability-unavailable: transferred expansions at analysis,
saved AOT output at namespace initialization or function invocation.
There is no fallback or enforcement reselection. If value-class capability
is unavailable, the first guard throws :value-capability-unavailable.:array-allocation: a failed value-array round trip gives
:failed-self-check. There is no fallback: make-array and into-array
throw :array-unavailable.The value-class switch reason is not a probe reason.
value-type.value-classes=off gives :disabled for :value-class,
:native-non-nil-field and :array-allocation, and no probe runs.
report reads the value-type.fallback policy before it runs a probe. An
invalid value makes report throw before any probe; no partial report is
returned. Loading value-type.capability does not load value-type.core and
needs no JEP 401 support, preview flag, export or java.lang.classfile.
Results are cached per loaded runtime; set JVM flags before startup.
Use an explicit concrete-class tag and :non-nil metadata:
(defvaluetype Point [^long x ^long y])
(defvaluetype Segment [^Point ^:non-nil start
^{:tag Point :non-nil true} end])
(defvaluetype Label [^String ^:non-nil text])
Only true enables the constraint. Absent, false and nil metadata retain ordinary
field behavior; other values are definition errors. The removed :nil-restricted
key is an error even when its value is false or nil. There is no compatibility alias.
The tag must name an already defined concrete, non-primitive class. Identity classes, value classes, fallen-back defvaluetype classes and external classes are eligible. Interfaces, abstract classes, arrays, forward references and enclosing types are not eligible. Mutable fields remain unsupported. Plain hints and disabled constraints keep ordinary type-hint and primitive-field behavior.
Factories, direct Clojure and Java constructors, and reflective constructors reject
nil with NullPointerException and wrong types with ClassCastException.
Reflection can wrap these exceptions in InvocationTargetException. Constrained
fields have final storage of the declared concrete type, but reference constructor
parameters remain Object. Constructor order and arity do not change. Nested objects
can contain ordinary fields that permit nil. Error text and the failure order for
multiple invalid arguments are not part of the contract.
Class representation and field enforcement are separate choices:
| Representation | Field enforcement |
|---|---|
| Identity fallback | Generated constructor checks |
| Value, with an eligible tag and native field capability | Native restriction |
| Value, otherwise | Generated constructor checks |
The fallback policy controls identity representation, not the enforcement choice. Missing native field support alone does not warn or fail under strict policy. Generated checks run in actual constructors, not only in factories. Reads add no policy checks. Unexpected probe, loader, verification and transformation failures remain errors; they do not select weaker output.
Requiring value-type.core is safe on Java 8 and preview-disabled EA3.
Identity output does not require optional JDK interfaces or preview. Value
output requires preview; field construction needs no internal-package export.
Real-JVM evidence covers the primary builds in
the JVM facts with Clojure 1.12.6. Controlled capability cases do not establish support for other JVMs.
See the runtime instructions for verification
commands and limits.
Field declaration errors are ExceptionInfo with :value-type/error,
:class-name and :reason. Invalid fields use :invalid-field and include
:field. The :invalid-field reasons are:
:removed-nil-restricted and :invalid-non-nil (with
:value); :missing-tag, :unresolved-tag, :enclosing-type,
:primitive-type, :array-type, :interface-type and :abstract-type
(with :tag).:identity-type (with :tag). The tag
names an identity class.:field-class-mismatch. The tag resolves to a different Class in the
compilation loader.Compiler operations can wrap errors in CompilerException. Declarations
are validated before enforcement selection. Discarded expansions still validate
declarations and apply representation policy.
AOT fixes both representation and enforcement. Saved owners and companions work without producer source with separate Clojure and Java callers. Identity artifacts retain generated checks on capable JVMs; guarded forms still apply receiving fallback policy. Top-level guards run during namespace initialization; function-contained guards run on invocation. Direct Java class access does not execute namespace policy guards, but constructors still enforce field constraints. Value artifacts require the same JDK feature release with preview enabled. Java 8 and preview-disabled EA3 reject them under every policy. There is no load-time conversion or enforcement reselection.
The contract excludes reflective mutation with access checks suppressed, Unsafe, instrumentation, and allocation or restoration that bypasses constructors. Neither enforcement mode guarantees flat storage or better performance. Array allocation retains its separate native-only capability boundary.
deftype* formsLibraries that emit deftype* can use value-type.core/defvaluetype*. It
takes the deftype* arguments and evaluates to the defined Class. Use it in
the same top-level position as deftype*, not inside a method body.
(value-type.core/defvaluetype* my.ns/Point my.ns.Point [x y]
:implements [clojure.lang.IType])
Two field shapes are accepted:
__meta, __extmap, __hash,
__hasheq; the namespace is ignored).__meta __extmap ^int __hash ^int __hasheq,
unqualified, with no tag on __meta and __extmap. This is the shape that
the Clojure compiler gives defrecord.The option :value-type/closed true, in the deftype* option position,
selects the closed record shape. false, nil and an absent option do not
select it; the form keeps the plain or record shape. The option requires the
record shape. When the option occurs more than once, the last occurrence
applies, as in deftype*. defvaluetype* removes each occurrence of the
option from the deftype* call.
Fields can be non-nil, as in
portable non-nil fields, in both shapes. A
hidden-name field cannot be non-nil. The main constructor checks each enabled
field. All constructors, create, record literals, compiled record constants
and reflection reject nil with NullPointerException and wrong types with
ClassCastException. The compiler, class initialization and reflection can
wrap these exceptions (for example in CompilerException,
ExceptionInInitializerError or InvocationTargetException); find the
exception in the cause chain. The error text is not specified.
These declaration checks run at macroexpansion, before representation policy, in this order; the first failure wins:
:mutable-field.:invalid-field with :reason :record-shape.:value-type/closed value other than true, false or nil gives
:invalid-option with :reason :invalid-value; :value-type/closed true
on a plain-shape form gives :reason :record-shape-required (data
:option, :value).:nil-restricted metadata gives :removed-nil-restricted; a :non-nil
value other than true, false or nil gives :invalid-non-nil.:non-nil true gives :reason :hidden-field.defvaluetype for enabled fields.An invalid form never selects fallback. Identity output with enabled fields or
the closed shape gives :unsupported-shape when a delegating constructor
writes an enabled field, or when a constructor both creates and calls the
target.
Caller obligations for record shapes:
__hash or __hasheq.create from a map with no entry for an
enabled field fails, and an assoc that calls a constructor (as
defrecord does) rejects nil for an enabled field. Code that builds
records from partial maps must supply each enabled field.Caller obligations for closed shapes, in addition:
__meta or __extmap.withMeta or assoc methods that use them.Value output removes both hash fields and ignores those arguments. The
compiler-generated constructors, create and getBasis keep their
contracts, so record literals and compiled record constants work. Methods
must compute hashes on each call. Owner or companion code that accesses a
removed field gives :invalid-field with :reason :removed-field-access.
Reflective access is not checked. Identity output keeps both fields; they hold
0 when callers meet these obligations. value-type does not validate the
hash arguments.
For a closed form, value output removes __meta, __extmap, __hash and
__hasheq; constructors keep their descriptors, and owner or companion
access to any of the four fields gives :removed-field-access. Identity
output keeps all four fields; __meta and __extmap hold nil. In both
outputs, the main constructor throws IllegalArgumentException for a
non-nil __meta or __extmap argument, before the non-nil field checks.
Thus create and the map literal reject an undeclared key. The positional
literal, the constructor with only the declared fields and compiled record
constants are not affected. The compiler, the reader and reflection can wrap
the exception. The error text is not specified.
Representation policy, fallback and AOT behave as for defvaluetype. The
fallback expansion with neither enabled fields nor the closed shape is guarded
deftype* output with no transform. With enabled fields or the closed shape,
the value and fallback expansions are receiving forms that repeat the
declaration checks; the fallback receiver gives identity output with generated
checks. Receivers of a transferred fallback expansion apply their own policy.
Every value expansion is a receiving form; it never selects fallback. See
the receiving boundary paragraph in Differences from
deftype: separate eval of the expansion works,
and only a full expansion outside compilation (for example
clojure.walk/macroexpand-all) gives :not-installed. An unexpected
constructor shape gives :unexpected-constructor.
Exclusions: mutable caches, value-equality emulation,
changes to defrecord and serialization guarantees. Real-JVM evidence covers
Clojure 1.12.6 and the primary Java 8 and Valhalla builds in
the JVM facts, with preview off and on for Valhalla. It does not establish other JVMs, Clojure versions, object size or
performance.
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |