Run core.memoize's test suite on jolt

Shaking out clojure.core.memoize (207 assertions, 0 fail) cleared several
general gaps:

- deref/@ on a deftype or reify implementing clojure.lang.IDeref dispatches to
  its deref method (RetryingDelay / make-derefable).
- deftype mutable fields (^:unsynchronized-mutable / ^:volatile-mutable) are
  read live: a set! within a method is observed by a later read in the same
  invocation, not the entry-time capture. Needed for double-checked locking.
  Immutable fields stay let-bound. Field reads rewrite to (.-field inst) with
  lexical-shadow tracking.
- def metadata values are evaluated, like Clojure: ^{:k (f)} stores (f)'s
  result and ^{:af some-fn} the fn. :tag stays a literal hint.
- try dispatches catch clauses by class in order via the exception supertype
  hierarchy; a non-matching value re-throws, an untyped host condition is caught
  by a RuntimeException/Exception/Throwable clause. Previously the last clause
  won and the class was ignored.
- locking takes a real per-object monitor (recursive Chez mutex) now that
  futures/agents/threads share one heap; it was a no-op.
- supers/ancestors reflect a small modeled JVM interface hierarchy, so
  (ancestors (class f)) yields Runnable/Callable (core.memoize's arg check).
- AssertionError / Error constructors.

JOLT_FEATURES is gone from the docs: it isn't read anywhere on Chez, and the
reader already includes :clj in its default feature set. RFC 0002's
{:jolt :default} design was reverted in the reader; docs now match the code.

Raises the SCI floor 205 -> 210.
This commit is contained in:
Yogthos 2026-06-25 13:23:05 -04:00
parent 3dde290f1a
commit d21ab77e7e
18 changed files with 1179 additions and 939 deletions

View file

@ -57,7 +57,10 @@ aren't implemented; a few are accepted but no-ops (noted inline).
`.hashCode` `.equals` `.getClass` work on any value.
- **`java.lang.Class`** — `forName` (throws a catchable `ClassNotFoundException`
for a class jolt can't back, so `(try (Class/forName "opt.Dep") (catch …))`
dependency probes work).
dependency probes work). There is no reflection, but a few common interfaces
carry a modeled ancestry so `(supers c)` / `(ancestors c)` answer like the JVM —
e.g. `(ancestors (class f))` for a function yields `Runnable` and `Callable`,
the check `core.memoize` uses to validate a memoizable argument.
### Strings and text
@ -138,8 +141,13 @@ aren't implemented; a few are accepted but no-ops (noted inline).
`IllegalArgumentException` `IllegalStateException` `IOException`
`NumberFormatException` `ArithmeticException` `NullPointerException`
`ClassCastException` `IndexOutOfBoundsException` `FileNotFoundException`
`UnsupportedOperationException` and the common network exceptions, each with
the `(E.)` / `(E. msg)` / `(E. msg cause)` / `(E. cause)` constructors.
`UnsupportedOperationException` `Error` `AssertionError` and the common network
exceptions, each with the `(E.)` / `(E. msg)` / `(E. msg cause)` / `(E. cause)`
constructors. `try` dispatches its `catch` clauses by class in order, respecting
the exception supertype hierarchy (`(catch Exception e …)` catches a
`RuntimeException` but not an `Error`); a thrown value matching no clause
re-throws. An untyped host condition (e.g. from `(/ 1 0)`) is caught by a
`RuntimeException`/`Exception`/`Throwable` clause.
What's deliberately absent: STM (`clojure.lang.LockingTransaction/isRunning`
returns `false`), reflection, `gen-class`/`proxy` of Java classes, and

View file

@ -1,9 +1,8 @@
# Clojure libraries known to work with Jolt
Libraries confirmed to load and pass their conformance checks on Jolt. A library
listed here works; some need `JOLT_FEATURES` including `clj` (noted below). See
the [examples](https://github.com/jolt-lang/examples), e.g. the
[ring-app example](https://github.com/jolt-lang/examples/tree/main/ring-app).
listed here works. See the [examples](https://github.com/jolt-lang/examples),
e.g. the [ring-app example](https://github.com/jolt-lang/examples/tree/main/ring-app).
* [aero](https://github.com/juxt/aero) — EDN configuration with tag literals
(`#ref`/`#env`/`#or`/`#profile`/`#long`/…)
@ -20,7 +19,7 @@ the [examples](https://github.com/jolt-lang/examples), e.g. the
[jolt-lang/jolt-crypto](https://github.com/jolt-lang/jolt-crypto) (OpenSSL)
* [reitit-core](https://github.com/metosin/reitit) — data-driven routing; the
`reitit.Trie` Java class is mirrored by
[jolt-lang/router](https://github.com/jolt-lang/router). `JOLT_FEATURES` `clj`.
[jolt-lang/router](https://github.com/jolt-lang/router).
* [integrant](https://github.com/weavejester/integrant) — data-driven system
configuration (`#ig/ref`), with its
[dependency](https://github.com/weavejester/dependency) and
@ -36,7 +35,7 @@ the [examples](https://github.com/jolt-lang/examples), e.g. the
* [migratus](https://github.com/yogthos/migratus) — database migrations over the
next.jdbc layer
* [malli](https://github.com/metosin/malli) — data schema validation, on the
malli-app example. `JOLT_FEATURES` `clj`.
malli-app example.
* [markdown-clj](https://github.com/yogthos/markdown-clj) — Markdown → HTML, on the
markdown-app example
* [hiccup](https://github.com/weavejester/hiccup) — HTML from Clojure data, on the
@ -44,11 +43,11 @@ the [examples](https://github.com/jolt-lang/examples), e.g. the
* [clojure.data.json](https://github.com/clojure/data.json) — JSON reading and writing
* [clojure.spec.alpha](https://github.com/clojure/spec.alpha) — data specs
* [core.match](https://github.com/clojure/core.match) — pattern matching.
`JOLT_FEATURES` `clj`.
* [core.cache](https://github.com/clojure/core.cache) — caching (Basic/FIFO/LRU/
LU/TTL/Soft + the wrapped atom API), over
[data.priority-map](https://github.com/clojure/data.priority-map).
`JOLT_FEATURES` `clj`.
* [core.memoize](https://github.com/clojure/core.memoize) — function memoization
over [core.cache](https://github.com/clojure/core.cache).
* [tick](https://github.com/juxt/tick) — date/time over Jolt's `java.time`;
`#time/…` literals via `time-literals`. `JOLT_FEATURES` `clj`.
`#time/…` literals via `time-literals`.
* [transit-jolt](https://github.com/jolt-lang/transit-jolt) — Transit (JSON) read/write

View file

@ -1,9 +1,22 @@
# RFC 0002 — Reader-Conditional Feature Set
- **Status**: Accepted (implemented; measured)
- **Status**: Superseded (2026-06-25) — jolt now includes `:clj` in the default
set; see the note below.
- **Created**: 2026-06-10
- **Spec**: `docs/spec/02-reader.md` §2.3 S18
> **Update (2026-06-25).** The default set is now **`#{:jolt :clj :default}`** —
> `:clj` is satisfied by default. The clj ecosystem's `.cljc` libraries gate
> their host code behind `#?(:clj …)` with no `:jolt`/`:default` fallback, so
> the conformance libraries (core.cache, core.match, tick, malli, …) only load
> with `:clj` present; requiring an opt-in for each was friction with no payoff
> once jolt's `clojure.lang.*`/`java.*` emulation was broad enough to run those
> `:clj` branches. Matching is still by **clause order**, so a library can place
> a `:jolt` branch first to override. There is no `JOLT_FEATURES` environment
> variable; a loading context overrides the set at runtime with
> `reader-features-set!`. The rest of this RFC is the original (reverted)
> design.
## Summary
jolt's reader-conditional feature set is **`#{:jolt :default}`**, matched in

View file

@ -159,9 +159,10 @@ checks → UNVERIFIED (rows to add).
key the platform satisfies wins (`#?(:default 5 :clj 6)` is `5` everywhere)
— not by key priority. Implementations SHOULD provide a per-loading-context
compatibility override for foreign-dialect libraries. (jolt:
`#{:jolt :default}`, opt-in via `reader-features-set!`/`JOLT_FEATURES`;
decision + A/B data in RFC 0002 — inheriting `:clj` cost 146 suite
assertions and 38 errors.)
`#{:jolt :clj :default}` — jolt emulates `clojure.lang.*`/`java.*`, so it
reads the `:clj` branch of a `.cljc` library by default; a library can put a
`:jolt` branch first to override, or a loading context can call
`reader-features-set!`. History in RFC 0002.)
- Reader conditionals MUST be an error outside `.cljc`-style reading unless
the implementation documents otherwise.