Step 4: lazy mapcat overlay — eliminate apply forcing on infinite inputs

mapcat in 10-seq.clj now uses a defn- mapcat-step helper that walks
lazy map results element-by-element (cons + lazy-seq), with explicit
arities for 1/2/3/4+ colls. The 4+-coll arity still uses apply to
spread colls to map, but apply no longer forces the inner map result.

Previously (apply concat (apply map f colls)) forced the lazy map
result through core-apply realize-for-iteration, which hung on
infinite inputs. The new step-based impl walks one element at a time
via first/rest/seq primitives.

Re-enabled deferred mapcat infinite-input harness case. 19/19 pass.

Gates: lazy-infinite 19/19, conformance 229/229 interpret+self-host
(compile 228/229 — 1 pre-existing protocol error), specs 32/32.
This commit is contained in:
Yogthos 2026-06-08 08:14:59 -04:00
parent e2e189acfe
commit d16e1f4eba
2 changed files with 25 additions and 5 deletions

View file

@ -23,7 +23,29 @@
(recur (conj ret (first s)) (next s)) (recur (conj ret (first s)) (next s))
(seq ret)))) (seq ret))))
(defn- mapcat-step [rs cur]
(lazy-seq
(if cur
(let [s (seq cur)]
(if s
(cons (first s) (mapcat-step rs (rest s)))
(mapcat-step rs nil)))
(let [s (seq rs)]
(if s
(let [c (first s)
sc (seq c)]
(if sc
(cons (first sc) (mapcat-step (rest s) (rest sc)))
(mapcat-step (rest s) nil)))
nil)))))
(defn mapcat (defn mapcat
([f] (comp (map f) cat)) ([f] (comp (map f) cat))
([f & colls] ([f coll]
(apply concat (apply map f colls)))) (mapcat-step (map f coll) nil))
([f c1 c2]
(mapcat-step (map f c1 c2) nil))
([f c1 c2 c3]
(mapcat-step (map f c1 c2 c3) nil))
([f c1 c2 c3 & colls]
(mapcat-step (apply map f c1 c2 c3 colls) nil)))

View file

@ -52,9 +52,7 @@
["take 3 partition-all 2 range" "(quote ((0 1) (2 3) (4 5)))" "(take 3 (partition-all 2 (range)))"] ["take 3 partition-all 2 range" "(quote ((0 1) (2 3) (4 5)))" "(take 3 (partition-all 2 (range)))"]
["take 3 map-indexed vector range" "(quote ([0 0] [1 1] [2 2]))" "(take 3 (map-indexed vector (range)))"] ["take 3 map-indexed vector range" "(quote ([0 0] [1 1] [2 2]))" "(take 3 (map-indexed vector (range)))"]
["take 3 distinct cycle" "(quote (1 2 3))" "(take 3 (distinct (cycle [1 2 1 3 1])))"] ["take 3 distinct cycle" "(quote (1 2 3))" "(take 3 (distinct (cycle [1 2 1 3 1])))"]
# NOTE: mapcat on infinite inputs hangs because apply forces the lazy map ["take 6 mapcat dup range" "(quote (0 0 1 1 2 2))" "(take 6 (mapcat (fn [x] [x x]) (range)))"]
# result. This requires Step 4 (fix apply to lazily spread lazy-seqs).
# ["take 6 mapcat dup range" "(quote (0 0 1 1 2 2))" "(take 6 (mapcat (fn [x] [x x]) (range)))"]
["take 3 take-nth 2 range" "(quote (0 2 4))" "(take 3 (take-nth 2 (range)))"] ["take 3 take-nth 2 range" "(quote (0 2 4))" "(take 3 (take-nth 2 (range)))"]
["take 3 interpose :x range" "(quote (0 :x 1))" "(take 3 (interpose :x (range)))"] ["take 3 interpose :x range" "(quote (0 :x 1))" "(take 3 (interpose :x (range)))"]
["take 3 map vector range iterate" "(quote ([0 100] [1 101] [2 102]))" "(take 3 (map vector (range) (iterate inc 100)))"] ["take 3 map vector range iterate" "(quote ([0 100] [1 101] [2 102]))" "(take 3 (map vector (range) (iterate inc 100)))"]