core: Phase 4 — move reduce-kv to the overlay, fixing the vector case

reduce-kv is pure over reduce/keys/get/nth, so it moves to 20-coll with no
host surface. Both branches fold through reduce, which fixes two latent bugs
in the Janet version: on a vector it saw the pvec as a table and folded over
its internal keys instead of the indices, and it ignored reduced entirely.
nil folds to init. Added vector-index, reduced, and nil spec cases plus a
3-mode conformance case for the vector fix.
This commit is contained in:
Yogthos 2026-06-07 20:05:25 -04:00
parent e6ff4b8a77
commit fcdf0ff535
4 changed files with 16 additions and 8 deletions

View file

@ -225,3 +225,14 @@
;; namespace-munge: Clojure namespace name -> legal Java package name (- -> _). ;; namespace-munge: Clojure namespace name -> legal Java package name (- -> _).
(defn namespace-munge [s] (defn namespace-munge [s]
(apply str (map (fn [c] (if (= c \-) \_ c)) (seq (str s))))) (apply str (map (fn [c] (if (= c \-) \_ c)) (seq (str s)))))
;; reduce-kv over a map (k v) or vector (index v). Both branches go through reduce,
;; so reduced short-circuits — and the vector path indexes correctly. (The prior
;; Janet version saw a pvec as a table and folded over its internal keys; it also
;; ignored reduced.) nil folds to init, matching Clojure.
(defn reduce-kv [f init coll]
(cond
(vector? coll) (reduce (fn [acc i] (f acc i (nth coll i))) init (range (count coll)))
(map? coll) (reduce (fn [acc k] (f acc k (get coll k))) init (keys coll))
(nil? coll) init
:else (throw (str "reduce-kv not supported on: " coll))))

View file

@ -1296,13 +1296,7 @@
(defn cstep [i] (fn [] @[(in c (% i (length c))) (cstep (+ i 1))])) (defn cstep [i] (fn [] @[(in c (% i (length c))) (cstep (+ i 1))]))
(make-lazy-seq (cstep 0)))))) (make-lazy-seq (cstep 0))))))
(defn core-reduce-kv [f init m] # reduce-kv now lives in the Clojure collection tier (core/20-coll.clj).
(var acc init)
(cond
(phm? m) (each e (phm-entries m) (set acc (f acc (in e 0) (in e 1))))
(or (struct? m) (table? m)) (each k (keys m) (set acc (f acc k (get m k))))
(indexed? m) (do (var i 0) (each x m (set acc (f acc i x)) (++ i))))
acc)
# pop is defined only on stacks (vectors -> last end, lists -> front); Clojure # pop is defined only on stacks (vectors -> last end, lists -> front); Clojure
# throws on sets/maps/seqs/strings/scalars. (peek lives in the Clojure kernel # throws on sets/maps/seqs/strings/scalars. (peek lives in the Clojure kernel
@ -2765,7 +2759,6 @@
"keep-indexed" core-keep-indexed "keep-indexed" core-keep-indexed
"map-indexed" core-map-indexed "map-indexed" core-map-indexed
"cycle" core-cycle "cycle" core-cycle
"reduce-kv" core-reduce-kv
"pop" core-pop "pop" core-pop
"trampoline" core-trampoline "trampoline" core-trampoline
"format" core-format "format" core-format

View file

@ -108,6 +108,7 @@
["subvec" "[2 3]" "(subvec [1 2 3 4 5] 1 3)"] ["subvec" "[2 3]" "(subvec [1 2 3 4 5] 1 3)"]
["subvec to-end" "[3 4 5]" "(subvec [1 2 3 4 5] 2)"] ["subvec to-end" "[3 4 5]" "(subvec [1 2 3 4 5] 2)"]
["reduce-kv" "{:a 2 :b 3}" "(reduce-kv (fn [m k v] (assoc m k (inc v))) {} {:a 1 :b 2})"] ["reduce-kv" "{:a 2 :b 3}" "(reduce-kv (fn [m k v] (assoc m k (inc v))) {} {:a 1 :b 2})"]
["reduce-kv vector idx" "(quote ([0 :a] [1 :b]))" "(reduce-kv (fn [a i v] (conj a [i v])) [] [:a :b])"]
### ---- iterating maps yields entries ---- ### ---- iterating maps yields entries ----
["map over map" "true" "(= #{1 2} (set (map val {:a 1 :b 2})))"] ["map over map" "true" "(= #{1 2} (set (map val {:a 1 :b 2})))"]

View file

@ -56,6 +56,9 @@
["reduce single no init" "5" "(reduce + [5])"] ["reduce single no init" "5" "(reduce + [5])"]
["reduced short-circuits" "3" "(reduce (fn [a x] (if (> a 2) (reduced a) (+ a x))) 0 [1 2 3 4 5])"] ["reduced short-circuits" "3" "(reduce (fn [a x] (if (> a 2) (reduced a) (+ a x))) 0 [1 2 3 4 5])"]
["reduce-kv" "6" "(reduce-kv (fn [a k v] (+ a v)) 0 {:a 1 :b 2 :c 3})"] ["reduce-kv" "6" "(reduce-kv (fn [a k v] (+ a v)) 0 {:a 1 :b 2 :c 3})"]
["reduce-kv on vector" "[[0 :a] [1 :b]]" "(reduce-kv (fn [a i v] (conj a [i v])) [] [:a :b])"]
["reduce-kv honors reduced" "[:a]" "(reduce-kv (fn [a i v] (if (= i 1) (reduced a) (conj a v))) [] [:a :b :c])"]
["reduce-kv on nil" "0" "(reduce-kv (fn [a k v] (+ a v)) 0 nil)"]
["reductions" "[1 3 6]" "(reductions + [1 2 3])"] ["reductions" "[1 3 6]" "(reductions + [1 2 3])"]
["mapcat" "[1 1 2 2]" "(mapcat (fn [x] [x x]) [1 2])"] ["mapcat" "[1 1 2 2]" "(mapcat (fn [x] [x x]) [1 2])"]
["keep" "[1 3]" "(keep (fn [x] (if (odd? x) x nil)) [1 2 3 4])"] ["keep" "[1 3]" "(keep (fn [x] (if (odd? x) x nil)) [1 2 3 4])"]