core: Stage 3 — leaf batch 2: sixteen more seed fns to the overlay; retire MIGRATION.md

key/val/select-keys/zipmap/merge/merge-with/get-in/memoize/partial/
trampoline/some?/true?/false?/max/min/reverse move to 20-coll.clj as the
canonical Clojure definitions, plus find — which was previously missing from
jolt entirely (select-keys/merge-with/memoize build on it). Two behavior
fixes ride along: memoize now caches nil results (the kernel fn re-computed
them — canonical find-based impl), and conj of nil onto a map is a no-op as
in Clojure (it errored; the canonical merge relies on it). max/min keep the
JVM NaN behavior by construction (pairwise >/<). not= stays: the kernel tier
(subvec) uses it.

One new tier-ordering rule, learned the hard way: a tier may only use macros
from tiers that load BEFORE it — memoize's if-let (30-macros) broke compiled
init while interpret mode passed, because compile expands macros at tier
load and the interpreter expands lazily. Now documented in the migration
workflow note.

MIGRATION.md is gone — task tracking lives in beads (jolt-ded; the per-batch
workflow, tier-order rules, perf wall, and remaining candidates are in bd
memory core-migration-workflow). The doc's candidate lists had gone stale
against the actual seed anyway.

43 new spec rows. Gate green: conformance 326x3, suite >= baseline, full
jpm test, bench at parity with main back-to-back (4851 vs 4831 TOTAL).
This commit is contained in:
Yogthos 2026-06-10 15:16:47 -04:00
parent af34d94870
commit 0e71b193e5
4 changed files with 175 additions and 266 deletions

View file

@ -54,3 +54,51 @@
["munge dashes" "\"a_b\"" "(munge \"a-b\")"]
["munge symbol" "\"x_y\"" "(munge (quote x-y))"]
["test no-test" ":no-test" "(test (quote foo))"])
# Phase 2 leaf batch 2 (jolt-ded): canonical ports of key/val/select-keys/
# zipmap/merge/merge-with/get-in/memoize/partial/trampoline/some?/true?/false?/
# max/min/reverse, plus find (previously missing entirely).
(defspec "clojure.core / leaf batch 2"
["key" "1" "(key (first {1 :a}))"]
["val" ":a" "(val (first {1 :a}))"]
["key non-entry throws" :throws "(key 5)"]
["find hit" "[:a 1]" "(find {:a 1} :a)"]
["find miss" "nil" "(find {:a 1} :b)"]
["find nil value" "[:a nil]" "(find {:a nil} :a)"]
["find on vector" "[0 :x]" "(find [:x :y] 0)"]
["select-keys" "{:a 1}" "(select-keys {:a 1 :b 2} [:a])"]
["select-keys nil val" "{:a nil}" "(select-keys {:a nil :b 2} [:a])"]
["select-keys missing" "{}" "(select-keys {:a 1} [:z])"]
["zipmap" "{:a 1 :b 2}" "(zipmap [:a :b] [1 2])"]
["zipmap uneven" "{:a 1}" "(zipmap [:a :b] [1])"]
["zipmap nil val" "{:a nil}" "(zipmap [:a] [nil])"]
["merge" "{:a 1 :b 2}" "(merge {:a 1} {:b 2})"]
["merge later wins" "{:a 2}" "(merge {:a 1} {:a 2})"]
["merge nil arg" "{:a 1}" "(merge {:a 1} nil)"]
["merge nil first" "{:a 1}" "(merge nil {:a 1})"]
["merge all nil" "nil" "(merge nil nil)"]
["merge empty" "nil" "(merge)"]
["merge entry pair" "{:a 1 :b 2}" "(merge {:a 1} [:b 2])"]
["merge-with" "{:a 3}" "(merge-with + {:a 1} {:a 2})"]
["merge-with disjoint" "{:a 1 :b 2}" "(merge-with + {:a 1} {:b 2})"]
["merge-with nil-val present" "{:a 1}" "(merge-with (fn [a b] (or a b)) {:a nil} {:a 1})"]
["get-in" "1" "(get-in {:a {:b 1}} [:a :b])"]
["get-in missing" ":nf" "(get-in {:a 1} [:z :y] :nf)"]
["get-in nil value present" "nil" "(get-in {:a {:b nil}} [:a :b] :nf)"]
["get-in empty path" "{:a 1}" "(get-in {:a 1} [])"]
["memoize" "2" "(do (def c (atom 0)) (def f (memoize (fn [x] (swap! c inc) x))) (f 1) (f 1) (f 2) (deref c))"]
["memoize caches nil" "1" "(do (def c (atom 0)) (def f (memoize (fn [x] (swap! c inc) nil))) (f 1) (f 1) (deref c))"]
["partial" "6" "((partial + 1 2) 3)"]
["partial no extra" "3" "((partial + 1 2))"]
["partial many fixed" "15" "((partial + 1 2 3 4) 5)"]
["trampoline" "10" "(trampoline (fn f [n acc] (if (zero? n) acc (fn [] (f (dec n) (+ acc 2))))) 5 0)"]
["some? true" "true" "(some? 0)"]
["some? false" "false" "(some? nil)"]
["true?/false?" "[true false false]" "[(true? true) (true? 1) (false? nil)]"]
["max" "3" "(max 1 3 2)"]
["min" "1" "(min 3 1 2)"]
["max single" "5" "(max 5)"]
["max non-number throws" :throws "(max 1 :a)"]
["reverse" "(quote (3 2 1))" "(reverse [1 2 3])"]
["reverse empty" "()" "(reverse nil)"]
["conj nil onto map" "{:a 1}" "(conj {:a 1} nil)"])