core: migrate 7 lazy seq fns from the Janet seed to the Clojure overlay (40-lazy)

Finishes a port the prior team started and reverted (bb4a3e0): the 40-lazy.clj
tier moved lazy seq fns Janet→Clojure but regressed the suite to 849 because
lazy-seq's expansion leaked as data in compile mode — that was jolt-r81, since
root-fixed (lazy-seq/lazy-cat moved to 00-syntax). With the wall gone, the port
works. This shrinks the Janet seed toward the north star (self-hosted
clojure-in-clojure on a minimal host bootstrap).

Moved to core/40-lazy.clj (wired as a loaded tier after 30-macros):
  distinct keep keep-indexed map-indexed cycle repeat iterate
40-lazy.clj completed to full parity: distinct gains its transducer arity;
keep/keep-indexed/map-indexed already had both arities.

Removed from the Janet seed (core.janet): the 7 core-* fns + their core-bindings
entries, the now-dead td-keep/td-map-indexed transducer helpers (the CLJ versions
carry their own), and the already-dead core-partition-by/core-xml-seq (shadowed by
10-seq/20-coll). Net: core.janet −131 lines.

Deferred (kept in Janet, separate follow-ups): partition-all (a CLJ port via
take/drop realizes a non-minimal element count, tripping the §6.3 laziness
counters + a suite file) and repeatedly (canonical CLJ doesn't validate args, so
the repeatedly.cljc throw cases regress). Both need behavior-matching first.

Gate: conformance 262x3, lazy-infinite 44/44, clojure-test-suite 4004/66 (UP from
3981 — the CLJ versions add coverage, e.g. distinct value-equality), fixpoint,
self-host, sci 422/0, fallback-zero, specs+unit, core-bench all green.
This commit is contained in:
Yogthos 2026-06-08 23:27:41 -04:00
parent 9e6abcd540
commit cd8f11934b
3 changed files with 39 additions and 145 deletions

View file

@ -3,8 +3,17 @@
;; ;;
;; Each fn ported from CLJS core.cljs, stripped of chunked-seq branches. ;; Each fn ported from CLJS core.cljs, stripped of chunked-seq branches.
;; --- distinct --- ;; --- distinct --- (transducer + lazy collection arity; value-based dedup)
(defn distinct [coll] (defn distinct
([]
(fn [rf]
(let [seen (volatile! #{})]
(fn ([] (rf)) ([result] (rf result))
([result input]
(if (contains? @seen input)
result
(do (vswap! seen conj input) (rf result input))))))))
([coll]
(let [step (fn step [xs seen] (let [step (fn step [xs seen]
(lazy-seq (lazy-seq
((fn [[f :as xs] seen] ((fn [[f :as xs] seen]
@ -13,7 +22,7 @@
(recur (rest s) seen) (recur (rest s) seen)
(cons f (step (rest s) (conj seen f)))))) (cons f (step (rest s) (conj seen f))))))
xs seen)))] xs seen)))]
(step coll #{}))) (step coll #{}))))
;; --- keep --- ;; --- keep ---
@ -76,10 +85,10 @@
(cstep 0))) (cstep 0)))
())) ()))
;; --- repeatedly --- ;; repeatedly stays in the Janet seed for now (core-repeatedly): the canonical CLJ
(defn repeatedly ;; version doesn't validate args, so (first (repeatedly non-fn)) / (repeatedly \a +)
([f] (lazy-seq (cons (f) (repeatedly f)))) ;; don't throw like the stricter Janet version (repeatedly.cljc throw cases).
([n f] (take n (repeatedly f)))) ;; Ported separately once the non-fn / non-number-count throws are matched.
;; --- repeat --- ;; --- repeat ---
(defn repeat (defn repeat
@ -91,10 +100,7 @@
(lazy-seq (cons x (iterate f (f x))))) (lazy-seq (cons x (iterate f (f x)))))
;; --- partition-all --- ;; partition-all stays in the Janet seed for now (core-partition-all): it already
(defn partition-all ;; has the transducer + collection arities (jolt-cru), and a CLJ port realizes a
([n coll] (partition-all n n coll)) ;; different (non-minimal) element count via take/drop than the Janet one,
([n step coll] ;; tripping the §6.3 laziness counters + a suite file. Ported separately.
(lazy-seq
(when-let [s (seq coll)]
(cons (take n s) (partition-all n step (nthrest coll step)))))))

View file

@ -53,7 +53,8 @@
{:ns "clojure.core.00-kernel" :kernel true} {:ns "clojure.core.00-kernel" :kernel true}
{:ns "clojure.core.10-seq" :kernel false} {:ns "clojure.core.10-seq" :kernel false}
{:ns "clojure.core.20-coll" :kernel false} {:ns "clojure.core.20-coll" :kernel false}
{:ns "clojure.core.30-macros" :kernel false}]) {:ns "clojure.core.30-macros" :kernel false}
{:ns "clojure.core.40-lazy" :kernel false}])
(defn- eval-overlay-source [ctx src] (defn- eval-overlay-source [ctx src]
(var s src) (var s src)

View file

@ -804,9 +804,7 @@
(fn [rf] (fn [& a] (case (length a) 0 (rf) 1 (rf (a 0)) (fn [rf] (fn [& a] (case (length a) 0 (rf) 1 (rf (a 0))
(if (truthy? (pred (a 1))) (rf (a 0) (a 1)) (a 0)))))) (if (truthy? (pred (a 1))) (rf (a 0) (a 1)) (a 0))))))
(defn td-remove [pred] (td-filter (fn [x] (not (pred x))))) (defn td-remove [pred] (td-filter (fn [x] (not (pred x)))))
(defn td-keep [f] # td-keep removed: keep (incl its transducer arity) lives in core/40-lazy.clj.
(fn [rf] (fn [& a] (case (length a) 0 (rf) 1 (rf (a 0))
(let [v (f (a 1))] (if (nil? v) (a 0) (rf (a 0) v)))))))
(defn td-take [n] (defn td-take [n]
(fn [rf] (fn [rf]
(var left n) (var left n)
@ -829,10 +827,7 @@
(fn [& a] (case (length a) 0 (rf) 1 (rf (a 0)) (fn [& a] (case (length a) 0 (rf) 1 (rf (a 0))
(do (when (and dropping (not (truthy? (pred (a 1))))) (set dropping false)) (do (when (and dropping (not (truthy? (pred (a 1))))) (set dropping false))
(if dropping (a 0) (rf (a 0) (a 1)))))))) (if dropping (a 0) (rf (a 0) (a 1))))))))
(defn td-map-indexed [f] # td-map-indexed removed: map-indexed (incl transducer arity) lives in core/40-lazy.clj.
(fn [rf]
(var i -1)
(fn [& a] (case (length a) 0 (rf) 1 (rf (a 0)) (do (++ i) (rf (a 0) (f i (a 1))))))))
# Stateful windowing transducers. The 1-arg (completion) arity flushes a partial # Stateful windowing transducers. The 1-arg (completion) arity flushes a partial
# trailing window before delegating to rf's completion; matches Clojure. # trailing window before delegating to rf's completion; matches Clojure.
@ -1342,22 +1337,7 @@
(sort-by keyfn arr)) (sort-by keyfn arr))
(tuple/slice (tuple ;arr)))))) (tuple/slice (tuple ;arr))))))
(defn core-distinct [coll] # distinct now lives in the Clojure lazy tier (core/40-lazy.clj).
# Option A: always lazy. seen-set is captured once and shared across the chain.
(let [seen @{}]
(defn dstep [c]
(fn []
(var cur c) (var found false) (var result nil)
(while (and (not found) (not (seq-done? cur)))
(let [x (core-first cur)]
(set cur (core-rest cur))
(when (nil? (seen x))
(put seen x true)
(set found true)
(set result x))))
(if found @[result (dstep cur)] nil)))
(make-lazy-seq (dstep (lazy-from coll)))))
# group-by / frequencies now live in the Clojure collection tier # group-by / frequencies now live in the Clojure collection tier
# (core/20-coll.clj). # (core/20-coll.clj).
@ -1384,21 +1364,7 @@
nil))))) nil)))))
(make-lazy-seq (pstep (lazy-from coll))))) (make-lazy-seq (pstep (lazy-from coll)))))
(defn core-partition-by [f coll] # partition-by now lives in the Clojure seq tier (core/10-seq.clj).
(def f (as-fn f))
(var result @[])
(var part @[])
(var last-k nil)
(each x (realize-for-iteration coll)
(let [k (f x)]
(if (and last-k (deep= k last-k))
(array/push part x)
(do
(if (> (length part) 0) (array/push result (tuple/slice (tuple ;part))))
(set part @[x])
(set last-k k)))))
(if (> (length part) 0) (array/push result (tuple/slice (tuple ;part))))
result)
(defn core-partition-all [n & rest] (defn core-partition-all [n & rest]
(if (= 0 (length rest)) (td-partition-all n) (if (= 0 (length rest)) (td-partition-all n)
@ -1417,39 +1383,8 @@
(make-lazy-seq (pstep (lazy-from coll)))))) (make-lazy-seq (pstep (lazy-from coll))))))
(defn core-keep-indexed [f coll] # keep-indexed / map-indexed / cycle now live in the Clojure lazy tier
(def f (as-fn f)) # (core/40-lazy.clj).
# Option A: always lazy.
(defn kstep [c i]
(fn []
(var cur c) (var idx i) (var found false) (var result nil)
(while (and (not found) (not (seq-done? cur)))
(let [v (f idx (core-first cur))]
(++ idx)
(set cur (core-rest cur))
(when (not (nil? v))
(set found true)
(set result v))))
(if found @[result (kstep cur idx)] nil)))
(make-lazy-seq (kstep (lazy-from coll) 0)))
(defn core-map-indexed [f & rest]
(if (= 0 (length rest)) (td-map-indexed f)
(let [coll (in rest 0)]
# Option A: always lazy.
(defn mstep [c i]
(fn []
(if (seq-done? c) nil
@[(f i (core-first c)) (mstep (core-rest c) (+ i 1))])))
(make-lazy-seq (mstep (lazy-from coll) 0)))))
(defn core-cycle [coll]
(let [c (realize-for-iteration coll)]
(if (= 0 (length c))
(make-lazy-seq (fn [] nil))
(do
(defn cstep [i] (fn [] @[(in c (% i (length c))) (cstep (+ i 1))]))
(make-lazy-seq (cstep 0))))))
# reduce-kv now lives in the Clojure collection tier (core/20-coll.clj). # reduce-kv now lives in the Clojure collection tier (core/20-coll.clj).
@ -1495,20 +1430,7 @@
(+= i step)) (+= i step))
(tuple/slice (tuple ;result)))))) (tuple/slice (tuple ;result))))))
(defn core-repeat # repeat / iterate now live in the Clojure lazy tier (core/40-lazy.clj).
"(repeat x) -> infinite lazy seq of x; (repeat n x) -> n copies of x."
[a & rest]
(if (= 0 (length rest))
(do (defn rstep [] (fn [] @[a (rstep)])) (make-lazy-seq (rstep)))
(let [n a x (in rest 0)]
(var result @[]) (var i 0)
(while (< i n) (array/push result x) (++ i))
result)))
(defn core-iterate [f x]
"Lazy infinite sequence x, (f x), (f (f x)), ..."
(defn istep [v] (fn [] @[v (istep (f v))]))
(make-lazy-seq (istep x)))
(defn core-repeatedly (defn core-repeatedly
"(repeatedly f) -> infinite lazy seq of (f) calls; (repeatedly n f) -> n calls." "(repeatedly f) -> infinite lazy seq of (f) calls; (repeatedly n f) -> n calls."
@ -2408,26 +2330,7 @@
@[(core-first c) (istep (core-rest c) true)])))) @[(core-first c) (istep (core-rest c) true)]))))
(make-lazy-seq (istep (lazy-from coll) false))))) (make-lazy-seq (istep (lazy-from coll) false)))))
(defn core-keep # keep now lives in the Clojure lazy tier (core/40-lazy.clj).
"(keep f coll) — (f x) for each x, dropping nils. (keep f) is a transducer."
[f & rest]
(def f (as-fn f))
(if (= 0 (length rest))
(td-keep f)
(let [coll (in rest 0)]
# Option A: always lazy.
(defn kstep [c]
(fn []
(var cur c) (var found false) (var result nil)
(while (and (not found) (not (seq-done? cur)))
(let [v (f (core-first cur))]
(set cur (core-rest cur))
(when (not (nil? v))
(set found true)
(set result v))))
(if found @[result (kstep cur)] nil)))
(make-lazy-seq (kstep (lazy-from coll))))))
(defn core-empty [coll] (defn core-empty [coll]
(cond (cond
@ -2517,14 +2420,7 @@
# Iterator/enumeration seqs — Jolt has no Java iterators, so adapt to plain seq. # Iterator/enumeration seqs — Jolt has no Java iterators, so adapt to plain seq.
(defn core-enumeration-seq [x] (core-seq x)) (defn core-enumeration-seq [x] (core-seq x))
(defn core-iterator-seq [x] (core-seq x)) (defn core-iterator-seq [x] (core-seq x))
(defn core-xml-seq [root] # xml-seq now lives in the Clojure collection tier (core/20-coll.clj).
(def out @[])
(defn walk [n]
(array/push out n)
(when (and (core-map? n) (core-contains? n :content))
(each c (realize-for-iteration (core-get n :content)) (walk c))))
(walk root)
(tuple ;out))
(defn core-line-seq [rdr] (defn core-line-seq [rdr]
(if (string? rdr) (core-seq (string/split "\n" rdr)) nil)) (if (string? rdr) (core-seq (string/split "\n" rdr)) nil))
(defn core-re-matcher [re s] @{:jolt/type :jolt/matcher :re re :s s :pos 0}) (defn core-re-matcher [re s] @{:jolt/type :jolt/matcher :re re :s s :pos 0})
@ -2867,9 +2763,6 @@
"contains?" core-contains? "contains?" core-contains?
"count" core-count "count" core-count
"partition-all" core-partition-all "partition-all" core-partition-all
"keep-indexed" core-keep-indexed
"map-indexed" core-map-indexed
"cycle" core-cycle
"pop" core-pop "pop" core-pop
"trampoline" core-trampoline "trampoline" core-trampoline
"format" core-format "format" core-format
@ -2952,7 +2845,6 @@
"random-uuid" core-random-uuid "random-uuid" core-random-uuid
"interpose" core-interpose "interpose" core-interpose
"mapcat" core-mapcat "mapcat" core-mapcat
"keep" core-keep
"find" core-find "find" core-find
"transduce" core-transduce "transduce" core-transduce
"sequence" core-sequence "sequence" core-sequence
@ -2990,12 +2882,8 @@
"nth" core-nth "nth" core-nth
"sort" core-sort "sort" core-sort
"sort-by" core-sort-by "sort-by" core-sort-by
"distinct" core-distinct
"partition" core-partition "partition" core-partition
"partition-by" core-partition-by
"range" core-range "range" core-range
"repeat" core-repeat
"iterate" core-iterate
"repeatedly" core-repeatedly "repeatedly" core-repeatedly
"identity" core-identity "identity" core-identity
"constantly" core-constantly "constantly" core-constantly
@ -3103,7 +2991,6 @@
"test" core-test "test" core-test
"enumeration-seq" core-enumeration-seq "enumeration-seq" core-enumeration-seq
"iterator-seq" core-iterator-seq "iterator-seq" core-iterator-seq
"xml-seq" core-xml-seq
"line-seq" core-line-seq "line-seq" core-line-seq
"re-matcher" core-re-matcher "re-matcher" core-re-matcher
"bean" core-bean "bean" core-bean