The overlay defines transduce in clojure/core/22-coll.clj as a pure composition (xf (reduce xf init coll)), and it shadows the native jolt-transduce by load order. The compiled overlay version is already what gets baked into the seed, so the native binding in natives-transduce.ss was dead weight. transduce is not used by the self-hosted compiler and no overlay tier before 22-coll references it, so removing the native binding is safe. Re-minting produces a byte-identical seed, which proves the runtime is unchanged. sequence stays native (its transformer iterator drives the reduced box and lazy realization directly).
96 lines
4.8 KiB
Scheme
96 lines
4.8 KiB
Scheme
;; natives-transduce.ss — the transducer surface: volatiles, the `cat` transducer,
|
|
;; and sequence / transduce application.
|
|
;;
|
|
;; `sequence` and `transduce` are seed natives. The stateful transducer arities
|
|
;; (take-nth/map-indexed/partition-by/dedupe/distinct, all overlay) use
|
|
;; volatile!/vswap!/vreset!/deref, shimmed here.
|
|
;;
|
|
;; Volatiles are a native mutable box (jvol) — the overlay vreset!/vswap! drive a
|
|
;; volatile through jolt.host/ref-put!+get, but a Chez volatile is a record, not a
|
|
;; tagged table, so those overlay versions are overridden natively in
|
|
;; post-prelude.ss. transduce/sequence build on the existing into-xform / reduce-
|
|
;; seq machinery (natives-seq.ss / seq.ss). Loaded after those + atoms.ss (deref).
|
|
|
|
;; --- volatiles ---------------------------------------------------------------
|
|
(define-record-type jvol (fields (mutable v)) (nongenerative chez-jvol-v1))
|
|
(define (jolt-volatile! x) (make-jvol x))
|
|
(define (jolt-vreset! vol x) (jvol-v-set! vol x) x)
|
|
(define (jolt-vswap! vol f . args)
|
|
(let ((nv (apply jolt-invoke f (jvol-v vol) args))) (jvol-v-set! vol nv) nv))
|
|
(define (jolt-volatile-pred? x) (jvol? x))
|
|
;; deref reads a volatile too (partition-all/-by transducers @-deref their box).
|
|
(define %xf-deref jolt-deref)
|
|
(set! jolt-deref (lambda (x) (if (jvol? x) (jvol-v x) (%xf-deref x))))
|
|
|
|
(def-var! "clojure.core" "volatile!" jolt-volatile!)
|
|
(def-var! "clojure.core" "deref" jolt-deref)
|
|
|
|
;; --- sequence ----------------------------------------------------------------
|
|
;; transduce lives in the overlay (clojure/core/22-coll.clj): it's a pure
|
|
;; composition (xf (reduce xf init coll)) over reduce, so the Clojure version
|
|
;; lowers to the same code the native shim did. sequence stays native (below):
|
|
;; its transformer iterator drives the reduced box + lazy realization directly.
|
|
|
|
;; (sequence coll) -> a seq; (sequence xform coll) -> a LAZY seq of coll transformed
|
|
;; by xform. A transformer iterator (mirrors clojure.core's TransformerIterator):
|
|
;; pull one input at a time through (xform rf), where rf buffers each emitted value;
|
|
;; emit the buffer lazily, pulling more input only when it drains. So an infinite or
|
|
;; expensive source is consumed incrementally — (first (sequence (map inc) (range)))
|
|
;; returns at once. Honors `reduced` (stop pulling) and runs the 1-arg completion to
|
|
;; flush a stateful xform (partition-all / dedupe / a trailing partition).
|
|
(define (sequence-xf xform coll)
|
|
(let* ((buf (box '())) ; emitted values for the current step, reversed
|
|
(rf (case-lambda
|
|
(() jolt-nil)
|
|
((acc) acc)
|
|
((acc x) (set-box! buf (cons x (unbox buf))) acc)))
|
|
(xrf (jolt-invoke xform rf)))
|
|
;; advance the source until buf holds output or the input is drained+completed.
|
|
(define (fill src acc completed)
|
|
(let loop ((src src) (acc acc) (completed completed))
|
|
(cond
|
|
((pair? (unbox buf)) (values src acc completed))
|
|
(completed (values src acc #t))
|
|
((jolt-reduced? acc)
|
|
(jolt-invoke xrf (jolt-reduced-val acc)) ; completion may flush
|
|
(loop src (jolt-reduced-val acc) #t))
|
|
(else
|
|
(let ((s (jolt-seq src)))
|
|
(if (jolt-nil? s)
|
|
(begin (jolt-invoke xrf acc) (loop src acc #t)) ; complete -> flush
|
|
(loop (seq-more s) (jolt-invoke xrf acc (seq-first s)) completed)))))))
|
|
;; Resolve the next chunk now (one fill pulls just enough input to emit or to
|
|
;; exhaust), so the result is a real cseq | empty — `empty` is jolt-empty-list
|
|
;; at the top (so an empty result still prints "()") and jolt-nil inside a tail
|
|
;; (the cseq terminator). The TAILS stay lazy, so an infinite source is fine.
|
|
(define (step src acc completed empty)
|
|
(let-values (((src2 acc2 comp2) (fill src acc completed)))
|
|
(let ((out (reverse (unbox buf))))
|
|
(set-box! buf '())
|
|
(if (null? out)
|
|
empty
|
|
(let build ((o out))
|
|
(if (null? (cdr o))
|
|
(cseq-lazy (car o) (lambda () (step src2 acc2 comp2 jolt-nil)))
|
|
(cseq-lazy (car o) (lambda () (build (cdr o))))))))))
|
|
(step coll jolt-nil #f jolt-empty-list)))
|
|
|
|
(define jolt-sequence
|
|
(case-lambda
|
|
((coll) (jolt-seq coll))
|
|
((xform coll) (sequence-xf xform coll))))
|
|
|
|
(def-var! "clojure.core" "sequence" jolt-sequence)
|
|
|
|
;; --- cat ---------------------------------------------------------------------
|
|
;; cat transducer: each input item is itself a collection, concatenated into the
|
|
;; downstream reducing fn.
|
|
(define (jolt-cat rf)
|
|
(lambda a
|
|
(cond
|
|
((null? a) (jolt-invoke rf))
|
|
((null? (cdr a)) (jolt-invoke rf (car a)))
|
|
(else
|
|
(let loop ((xs (seq->list (jolt-seq (cadr a)))) (acc (car a)))
|
|
(if (null? xs) acc (loop (cdr xs) (jolt-invoke rf acc (car xs)))))))))
|
|
(def-var! "clojure.core" "cat" jolt-cat)
|