fix: functional concat + stateless multi-coll map

core-concat rewritten as functional: step(cs) returns pure thunk,
each rest-thunk captures its own (cs) state. No shared mutable
state — parallel paths through the same concat are independent.

core-map multi-coll rewritten as stateless: step(cs,idxs,reals)
returns thunk with fresh copies of all cursor arrays. No shared
mutable cursors — each invocation of the map step is independent.

ls-first/ls-rest handle :jolt/pending sentinel as nil.
Empty-cell guards prevent bounds errors on exhausted collections.

Verified: ones (infinite), map+ on ones, concat, all existing
tests pass. fib-seq produces [0 1] (2 elements) with clean cycle
break via sentinel. Full infinite self-referencing needs deeper
lazy-seq architecture (per-element LazySeq with sv sentinel).
This commit is contained in:
Yogthos 2026-06-04 13:02:12 -04:00
parent 1ef3c8db39
commit f271789eed

View file

@ -304,61 +304,58 @@
(defn core-map [f & colls] (defn core-map [f & colls]
(if (= 1 (length colls)) (if (= 1 (length colls))
# Single-collection: eagerly realized (common case, fine for most uses) # Single-collection: eagerly realized
(let [c (realize-for-iteration (colls 0)) (let [c (realize-for-iteration (colls 0))
result (do result (do
(var res @[]) (var res @[])
(each x c (array/push res (f x))) (each x c (array/push res (f x)))
res)] res)]
(if (tuple? c) (tuple/slice (tuple ;result)) result)) (if (tuple? c) (tuple/slice (tuple ;result)) result))
# Multi-collection: return a lazy-seq # Multi-collection: lazy-seq with per-element independent state
(let [n (length colls) (let [init-cs (array/new-filled (length colls) nil)
cursors (array/new-filled n nil) init-idxs (array/new-filled (length colls) 0)
idxs (array/new-filled n 0) init-reals (array/new-filled (length colls) nil)
realized (array/new-filled n nil)] _ (do
# Initialize: detect lazy-seqs vs realized collections (var i 0)
(var i 0) (while (< i (length colls))
(while (< i n) (let [c (in colls i)]
(let [coll (colls i)] (if (lazy-seq? c)
(if (lazy-seq? coll) (put init-cs i c)
(put cursors i coll) (do (put init-cs i nil) (put init-reals i c))))
(do (++ i))
(put realized i coll) nil)]
(put cursors i nil)))) (defn step [cs idxs reals]
(++ i)) "cs: current lazy-seq cursors, idxs: indices, reals: realized colls"
# Helper: get next element from collection i, advancing state. (fn []
# Returns the element or nil if exhausted. (var args @[])
(defn next-elem [i] (var next-cs (array/new-filled (length cs) nil))
(let [cur (cursors i) (var next-idxs (array/new-filled (length idxs) 0))
ridx (idxs i)] (var next-reals (array/new-filled (length reals) nil))
(if (not (nil? cur)) (var ok true)
(let [val (ls-first cur)] (var i 0)
(if (nil? val) nil (while (< i (length cs))
(do (let [cur (in cs i) ridx (in idxs i) real (in reals i)]
(put cursors i (ls-rest cur)) (if (not (nil? cur))
(put idxs i (+ ridx 1)) (let [val (ls-first cur)]
val))) (if (nil? val) (do (set ok false) (break))
(let [coll (realized i)] (do (array/push args val)
(if (nil? coll) (put next-cs i (ls-rest cur))
(let [rc (realize-for-iteration (colls i))] (put next-idxs i (+ ridx 1))
(put realized i rc) (put next-reals i nil))))
(if (>= ridx (length rc)) nil (let [c (if (nil? real)
(do (put idxs i (+ ridx 1)) (rc ridx)))) (let [rc (realize-for-iteration (in colls i))]
(if (>= ridx (length coll)) nil (put next-reals i rc) rc)
(do (put idxs i (+ ridx 1)) (coll ridx)))))))) real)]
# Build a lazy-seq of mapped results (if (>= ridx (length c)) (do (set ok false) (break))
(defn build-cell [] (do (array/push args (in c ridx))
(var args @[]) (put next-cs i nil)
(var i 0) (put next-idxs i (+ ridx 1))
(while (< i n) (put next-reals i c))))))
(let [v (next-elem i)] (++ i))
(if (nil? v) (break nil)) (if (and ok (= (length args) (length cs)))
(array/push args v)) @[(apply f args) (step next-cs next-idxs next-reals)]
(++ i)) nil)))
(if (= (length args) n) (make-lazy-seq (step init-cs init-idxs init-reals)))))
@[(apply f args) (fn [] (build-cell))]
nil))
(make-lazy-seq build-cell))))
(defn core-filter [pred coll] (defn core-filter [pred coll]
(var result @[]) (var result @[])
@ -463,21 +460,26 @@
nil))))) nil)))))
(defn core-concat [& colls] (defn core-concat [& colls]
"Lazy concatenation — returns a lazy-seq that yields elements one at a time." "Lazy concatenation. Each rest-thunk captures its own (cur, cs) state
(var cs (if (tuple? colls) (array/slice colls) colls)) so that independent traversal paths do not corrupt each other."
(var cur nil) (defn step [cs]
(defn next-cell [] (if (= 0 (length cs))
(var cell (coll->cells cur)) (fn [] nil)
(while (and (nil? cell) (not (nil? cs)) (> (length cs) 0)) (let [c (in cs 0)
(set cur (in cs 0)) remaining (array/slice cs 1)]
(set cs (array/slice cs 1)) (if (lazy-seq? c)
(set cell (coll->cells cur))) (let [val (ls-first c)]
(if (nil? cell) nil (if (nil? val) (step remaining)
@[(in cell 0) (let [rest-ls (ls-rest c)
(fn [] next-step (step (array/insert remaining 0 rest-ls))]
(set cur (in cell 1)) (fn [] @[val next-step]))))
(next-cell))])) (let [cell (coll->cells c)]
(make-lazy-seq next-cell)) (if (nil? cell) (step remaining)
(let [rest-fn (in cell 1)
next-step (if (nil? rest-fn) (step remaining)
(step (array/insert remaining 0 rest-fn)))]
(fn [] @[(in cell 0) next-step]))))))))
(make-lazy-seq (step (if (tuple? colls) (array/slice colls) colls))))
(defn core-reverse [coll] (defn core-reverse [coll]
(if (nil? coll) @[] (if (nil? coll) @[]