diff --git a/src/jolt/core.janet b/src/jolt/core.janet index 32d9b84..4fcfb0d 100644 --- a/src/jolt/core.janet +++ b/src/jolt/core.janet @@ -462,21 +462,33 @@ (defn core-concat [& colls] "Lazy concatenation — returns a lazy-seq that yields elements one at a time. - Supports self-referencing sequences like fib-seq." - (var cs (if (tuple? colls) (array/slice colls) colls)) - (var cur nil) - (defn next-cell [] - (var cell (coll->cells cur)) - (while (and (nil? cell) (not (nil? cs)) (> (length cs) 0)) - (set cur (in cs 0)) - (set cs (array/slice cs 1)) - (set cell (coll->cells cur))) - (if (nil? cell) nil - @[(in cell 0) + Fully functional: each rest thunk captures its own state, so multiple access + paths (e.g. fib-seq used simultaneously in (rest fib-seq) and fib-seq) work + without shared-mutable-state corruption." + (defn step [cs] + "Return a thunk that produces nil (end) or @[value, rest-thunk]." + (if (= 0 (length cs)) + (fn [] nil) + (let [c (in cs 0) + remaining (array/slice cs 1)] (fn [] - (set cur (in cell 1)) - (next-cell))])) - (make-lazy-seq next-cell)) + (let [cell (coll->cells c)] + (if (nil? cell) + ((step remaining)) + (let [value (in cell 0) + rest-fn (in cell 1)] + @[value + (fn [] + (if (nil? rest-fn) + ((step remaining)) + (let [new-cs (array/new-filled (+ 1 (length remaining)) nil)] + (put new-cs 0 rest-fn) + (var i 0) + (while (< i (length remaining)) + (put new-cs (+ i 1) (in remaining i)) + (++ i)) + ((step new-cs)))))]))))))) + (make-lazy-seq (step (if (tuple? colls) (array/slice colls) colls)))) (defn core-reverse [coll] (if (nil? coll) @[] diff --git a/src/jolt/phm.janet b/src/jolt/phm.janet index 326c8a1..8a802d9 100644 --- a/src/jolt/phm.janet +++ b/src/jolt/phm.janet @@ -130,23 +130,25 @@ (defn realize-ls "Force a LazySeq cell. Returns nil (empty) or [first-val, rest-thunk]. - If the thunk returns another lazy-seq, recursively realize it." + If the thunk returns another lazy-seq, recursively realize it. + Detects self-referencing cycles by setting :realized before calling thunk." [ls] (if (get ls :realized) (ls :val) - (let [raw ((ls :fn)) - v (if (lazy-seq? raw) (realize-ls raw) raw)] + (do (put ls :realized true) - (put ls :val v) - v))) + (let [raw ((ls :fn)) + v (if (lazy-seq? raw) (realize-ls raw) raw)] + (put ls :val v) + v)))) (defn ls-first [ls] (let [cell (realize-ls ls)] - (if (nil? cell) nil (in cell 0)))) + (if (or (nil? cell) (= 0 (length cell))) nil (in cell 0)))) (defn ls-rest [ls] (let [cell (realize-ls ls)] - (if (nil? cell) nil + (if (or (nil? cell) (= 0 (length cell))) nil (let [rt (in cell 1)] (if (nil? rt) nil (make-lazy-seq rt))))))