Real transient correctness gaps surfaced by the clojure-test-suite:
- Transients are now invokable for read-only lookup like their persistent forms:
((transient v) i), ((transient m) k [default]), (:k (transient m)),
((transient s) x). jolt-invoke/coll-lookup gained a transient branch
(transient-lookup) that indexes :arr / canon-keyed :tbl. Added phm/canon as a
public canonicalizer so collection keys compare by value here too.
- assoc! accepts an ODD arg count (a missing final value is nil), unlike assoc —
core-assoc! now uses nil-safe get for the value instead of erroring.
- Using a transient after persistent! (or a second persistent!, or pop! on an
empty transient vector) now throws, via a :jolt/persistent invalidation flag
checked by the mutating ops. Catches the classic transient footgun.
spec: transients-spec gains invokable-lookup (7), assoc!-odd-args (4),
invalidation (4). clojure-test-suite pass 1704->1719.
Remaining transient fails are accepted lenient divergences (jolt doesn't throw
on bad-shape conj!/assoc!/pop! of wrong-typed args; nil set elements). jpm test green.
Replace the correctness-only transient aliases with real mutable scratch
collections via host interop:
- transient vector -> a Janet array; conj!/assoc!/pop! mutate in place
- transient map -> a Janet table keyed by canonical key (collection keys still
compare by value); assoc!/dissoc!/conj! mutate in place
- transient set -> a Janet table; conj!/disj! mutate in place
- persistent! freezes back to a pvec / phm / phs
- count/nth/get/contains? work on transients; transient? predicate added
Building a map/set this way avoids the persistent path's per-step bucket-array
copying (transient map build ~35% faster at 20k here); vectors are comparable
since pvec conj is already ~O(1). The mutating ops return the transient and the
source collection is untouched.
spec/transients-spec (34 cases). conformance 218/218, jpm test green.