core: Phase 5 Option A — lazy interleave/reductions/tree-seq via letfn

These three were the last eager transformers, blocked by jolt-r81: a self-
recursive lazy-seq in the overlay leaks its macro expansion under :compile? when
recursion goes through a top-level name or (fn name …) self-name. Rewriting the
recursion as letfn-bound (the form partition-by/mapcat/dedupe already use, which
compiles cleanly) sidesteps the bug. All three are now lazy in interpret,
compile, and self-host — completing Option A for every transformer.

interleave: canonical lazy cons-recursion (2-arity) + map/concat (n-arity).
reductions: letfn step accumulator. tree-seq: letfn walk + lazy mapcat.

Gate: conformance 246x3, lazy-infinite 40/40 (+interleave/reductions/tree-seq
infinite cases), fixpoint, self-host, specs+unit green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Yogthos 2026-06-08 17:28:55 -04:00
parent 3a42438b68
commit c7e162add4
2 changed files with 49 additions and 40 deletions

View file

@ -151,36 +151,34 @@
(defn comparator [pred]
(fn [a b] (cond (pred a b) -1 (pred b a) 1 :else 0)))
;; Eager (Jolt has no laziness yet): a vector of the running accumulators.
;; Lazy: the running accumulators, one at a time (matches Clojure). Recursion is
;; letfn-bound (NOT top-level self-call) so the lazy-seq body compiles cleanly in
;; the overlay — see jolt-r81.
(defn reductions
([f coll]
(let [s (seq coll)]
(if s
(reductions f (first s) (rest s))
(list (f)))))
(lazy-seq
(let [s (seq coll)]
(if s
(reductions f (first s) (rest s))
(list (f))))))
([f init coll]
(loop [acc init xs (seq coll) out [init]]
(if xs
(let [a (f acc (first xs))] (recur a (next xs) (conj out a)))
out))))
(letfn [(step [acc s]
(cons acc
(lazy-seq
(let [s (seq s)]
(when s
(step (f acc (first s)) (rest s)))))))]
(step init coll))))
;; The lazy tree-seq (using lazy-seq/make-lazy-seq) correctly implements
;; Clojure semantics but triggers compile-mode issues in self-hosted compilation.
;; When compile mode is fixed, replace the eager version below with:
;; (defn tree-seq [branch? children root]
;; (let [walk (fn walk [node]
;; (lazy-seq
;; (cons node
;; (when (branch? node)
;; (mapcat walk (children node))))))]
;; (walk root)))
;; Lazy pre-order DFS (matches Clojure). letfn-bound walk (not (fn walk …)) so it
;; compiles cleanly in the overlay under :compile? — see jolt-r81.
(defn tree-seq [branch? children root]
(let [walk (fn walk [acc node]
(let [acc (conj acc node)]
(if (branch? node)
(reduce walk acc (children node))
acc)))]
(walk [] root)))
(letfn [(walk [node]
(lazy-seq
(cons node
(when (branch? node)
(mapcat walk (children node))))))]
(walk root)))
;; Canonical flatten via tree-seq: the leaves (non-sequential nodes) in order.
;; Flattens lists too (sequential?), matching Clojure/CLJS.
@ -191,19 +189,29 @@
(defn xml-seq [root]
(tree-seq (complement string?) (comp seq :content) root))
;; Eager interleave: round-robin one element from each coll until any exhausts.
;; A lazy version (canonical Clojure cons-recursion) hits the same compile-mode
;; overlay bug as reductions/tree-seq — a self-recursive lazy-seq leaks its macro
;; expansion under :compile? (see jolt-r81). Eager until that's fixed.
(defn interleave [& colls]
(if (empty? colls)
(list)
(let [cs (mapv vec colls)
n (apply min (map count cs))]
(loop [i 0 out []]
(if (< i n)
(recur (inc i) (reduce (fn [o c] (conj o (nth c i))) out cs))
out)))))
;; Lazy interleave: round-robin one element from each coll until any exhausts.
;; letfn-bound recursion (not top-level self-call) so the lazy-seq body compiles
;; cleanly in the overlay — see jolt-r81.
(defn interleave
([] ())
([c1] (lazy-seq c1))
([c1 c2]
(letfn [(step [s1 s2]
(lazy-seq
(let [s1 (seq s1) s2 (seq s2)]
(when (and s1 s2)
(cons (first s1)
(cons (first s2)
(step (rest s1) (rest s2))))))))]
(step c1 c2)))
([c1 c2 & cs]
(letfn [(step [ss]
(lazy-seq
(let [ss (map seq ss)]
(when (every? identity ss)
(concat (map first ss)
(step (map rest ss)))))))]
(step (list* c1 c2 cs)))))
;; No ratio type on Jolt, so rationalize is identity.
(defn rationalize [x] x)