seq fns are lazy by default, like Clojure (LazySeq, not eager-headed)
map/filter/remove/take/drop/concat/take-while/drop-while/mapcat/partition built an eager-headed cseq: the first element (and the fn application) ran at construction, so a side-effecting (map f coll) fired f immediately and (class (map …)) was PersistentList instead of LazySeq. This diverged from Clojure, which wraps the whole body in lazy-seq. It went unnoticed because the conformance gate certifies values, not realization — eager and lazy heads produce identical values — and unit.edn even baked PersistentList in as expected. test.check's for-all-takes-multiple-expressions (which counts side effects in a for-all body) exposed it. Wrap each native producer's result in a lazy-seq node so the body, incl. the first element, defers until forced — the forced cseq still has eager heads, so reduce/count/dorun/etc. force on walk and there's no per-element cost. dedupe's (seq coll) is moved inside its lazy-seq. A jolt LazySeq is now recognized by coll?/empty, the analyzer's form predicates (a macro can build its expansion with map), value-host-tags + instance? (LazySeq/ISeq/ Sequential), and reports clojure.lang.LazySeq. Kept the native Scheme implementations rather than porting Clojure's: a straight lazy-seq+cons port is 3x slower and Clojure's chunked fast path is 288x slower because jolt's chunk machinery is unoptimized (filed jolt-j9dz); the wrapped natives are Clojure-lazy at native speed. +12 corpus rows (laziness at construction, LazySeq type, both JVM-certified). make test + shakesmoke green, selfhost holds, 0 new divergences.
This commit is contained in:
parent
92368b49f1
commit
b879430618
10 changed files with 80 additions and 40 deletions
|
|
@ -46,7 +46,9 @@
|
||||||
;; ANY non-empty seq is a list form for analysis (a macro/eval form built via
|
;; ANY non-empty seq is a list form for analysis (a macro/eval form built via
|
||||||
;; concat/map/cons is a lazy cseq with list?=#f, but evaluating it still means
|
;; concat/map/cons is a lazy cseq with list?=#f, but evaluating it still means
|
||||||
;; calling its head) — not just reader-built lists.
|
;; calling its head) — not just reader-built lists.
|
||||||
(define (hc-list? x) (or (empty-list-t? x) (cseq? x)))
|
;; a lazy seq is a list form too: a macro that builds its expansion with map/for
|
||||||
|
;; (now a LazySeq, not an eager cseq) and splices it must still analyze.
|
||||||
|
(define (hc-list? x) (or (empty-list-t? x) (cseq? x) (jolt-lazyseq? x)))
|
||||||
(define (hc-vec? x) (pvec? x))
|
(define (hc-vec? x) (pvec? x))
|
||||||
(define (hc-map? x) (and (pmap? x) (jolt-nil? (jolt-get x hc-kw-jolt-type))))
|
(define (hc-map? x) (and (pmap? x) (jolt-nil? (jolt-get x hc-kw-jolt-type))))
|
||||||
;; A set form is the reader's tagged map {:jolt/type :jolt/set :value <pvec>} OR a
|
;; A set form is the reader's tagged map {:jolt/type :jolt/set :value <pvec>} OR a
|
||||||
|
|
@ -106,7 +108,7 @@
|
||||||
;; list items -> jolt vector (pvec); the analyzer mapv's over the result.
|
;; list items -> jolt vector (pvec); the analyzer mapv's over the result.
|
||||||
(define (hc-elements x)
|
(define (hc-elements x)
|
||||||
(cond ((empty-list-t? x) empty-pvec)
|
(cond ((empty-list-t? x) empty-pvec)
|
||||||
((cseq? x) (make-pvec (list->vector (seq->list x))))
|
((or (cseq? x) (jolt-lazyseq? x)) (make-pvec (list->vector (seq->list x))))
|
||||||
(else empty-pvec)))
|
(else empty-pvec)))
|
||||||
(define (hc-vec-items x) x) ; already a pvec
|
(define (hc-vec-items x) x) ; already a pvec
|
||||||
(define (hc-set-items x)
|
(define (hc-set-items x)
|
||||||
|
|
|
||||||
|
|
@ -751,6 +751,7 @@
|
||||||
((string=? iface "IPersistentSet") (or (pset? val) (htable-sorted-set? val)))
|
((string=? iface "IPersistentSet") (or (pset? val) (htable-sorted-set? val)))
|
||||||
((string=? iface "ISeq")
|
((string=? iface "ISeq")
|
||||||
(or (cseq? val) (empty-list-t? val) (jolt-lazyseq? val)))
|
(or (cseq? val) (empty-list-t? val) (jolt-lazyseq? val)))
|
||||||
|
((string=? iface "LazySeq") (jolt-lazyseq? val))
|
||||||
;; Seqable is anything (seq x) works on — every persistent
|
;; Seqable is anything (seq x) works on — every persistent
|
||||||
;; collection, not just seqs (a vector IS Seqable, not an ISeq).
|
;; collection, not just seqs (a vector IS Seqable, not an ISeq).
|
||||||
((string=? iface "Seqable")
|
((string=? iface "Seqable")
|
||||||
|
|
|
||||||
|
|
@ -105,8 +105,9 @@
|
||||||
(if (null? colls)
|
(if (null? colls)
|
||||||
(td-mapcat f)
|
(td-mapcat f)
|
||||||
;; lazily concat the per-element results — no seq->list, so mapcat over an
|
;; lazily concat the per-element results — no seq->list, so mapcat over an
|
||||||
;; infinite source stays lazy.
|
;; infinite source stays lazy; the outer lazy-seq node defers the first
|
||||||
(lazy-concat-seq (apply jolt-map f colls))))
|
;; element so a side-effecting f does not fire at construction (LazySeq).
|
||||||
|
(jolt-make-lazy-seq (lambda () (jolt-seq (lazy-concat-seq (apply jolt-map f colls)))))))
|
||||||
|
|
||||||
;; take-while / drop-while: 1-arg -> transducer; 2-arg -> a seq over the coll.
|
;; take-while / drop-while: 1-arg -> transducer; 2-arg -> a seq over the coll.
|
||||||
(define (take-while-seq pred s)
|
(define (take-while-seq pred s)
|
||||||
|
|
@ -118,7 +119,7 @@
|
||||||
(define jolt-take-while
|
(define jolt-take-while
|
||||||
(case-lambda
|
(case-lambda
|
||||||
((pred) (td-take-while pred))
|
((pred) (td-take-while pred))
|
||||||
((pred coll) (take-while-seq pred (jolt-seq coll)))))
|
((pred coll) (jolt-make-lazy-seq (lambda () (jolt-seq (take-while-seq pred (jolt-seq coll))))))))
|
||||||
(define (drop-while-seq pred coll)
|
(define (drop-while-seq pred coll)
|
||||||
(let loop ((s (jolt-seq coll)))
|
(let loop ((s (jolt-seq coll)))
|
||||||
(if (and (not (jolt-nil? s)) (jolt-truthy? (jolt-invoke pred (seq-first s))))
|
(if (and (not (jolt-nil? s)) (jolt-truthy? (jolt-invoke pred (seq-first s))))
|
||||||
|
|
@ -127,7 +128,7 @@
|
||||||
(define jolt-drop-while
|
(define jolt-drop-while
|
||||||
(case-lambda
|
(case-lambda
|
||||||
((pred) (td-drop-while pred))
|
((pred) (td-drop-while pred))
|
||||||
((pred coll) (drop-while-seq pred coll))))
|
((pred coll) (jolt-make-lazy-seq (lambda () (jolt-seq (drop-while-seq pred coll)))))))
|
||||||
|
|
||||||
;; partition: (partition n coll), (partition n step coll), or
|
;; partition: (partition n coll), (partition n step coll), or
|
||||||
;; (partition n step pad coll). Only complete partitions of size n are kept;
|
;; (partition n step pad coll). Only complete partitions of size n are kept;
|
||||||
|
|
@ -135,9 +136,9 @@
|
||||||
;; runs out). Each partition is a seq; the whole result is a lazy seq of seqs.
|
;; runs out). Each partition is a seq; the whole result is a lazy seq of seqs.
|
||||||
(define jolt-partition
|
(define jolt-partition
|
||||||
(case-lambda
|
(case-lambda
|
||||||
((n coll) (partition* (->idx n) (->idx n) #f #f coll))
|
((n coll) (jolt-make-lazy-seq (lambda () (jolt-seq (partition* (->idx n) (->idx n) #f #f coll)))))
|
||||||
((n step coll) (partition* (->idx n) (->idx step) #f #f coll))
|
((n step coll) (jolt-make-lazy-seq (lambda () (jolt-seq (partition* (->idx n) (->idx step) #f #f coll)))))
|
||||||
((n step pad coll) (partition* (->idx n) (->idx step) #t pad coll))))
|
((n step pad coll) (jolt-make-lazy-seq (lambda () (jolt-seq (partition* (->idx n) (->idx step) #t pad coll)))))))
|
||||||
(define (take-n n s) ; -> (values list-of-first-n remaining-seq taken-count)
|
(define (take-n n s) ; -> (values list-of-first-n remaining-seq taken-count)
|
||||||
(let loop ((n n) (s s) (acc '()))
|
(let loop ((n n) (s s) (acc '()))
|
||||||
(if (or (fx<=? n 0) (jolt-nil? s))
|
(if (or (fx<=? n 0) (jolt-nil? s))
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
;; seq, (rest list), (seq coll), (map …) are seqs but not lists.
|
;; seq, (rest list), (seq coll), (map …) are seqs but not lists.
|
||||||
(define (jolt-list-pred? x) (or (and (cseq? x) (cseq-list? x)) (empty-list-t? x)))
|
(define (jolt-list-pred? x) (or (and (cseq? x) (cseq-list? x)) (empty-list-t? x)))
|
||||||
(define (jolt-coll-pred? x)
|
(define (jolt-coll-pred? x)
|
||||||
(or (pvec? x) (pmap? x) (pset? x) (cseq? x) (empty-list-t? x)))
|
(or (pvec? x) (pmap? x) (pset? x) (cseq? x) (empty-list-t? x) (jolt-lazyseq? x)))
|
||||||
(define (jolt-number? x) (number? x))
|
(define (jolt-number? x) (number? x))
|
||||||
(define (jolt-string? x) (string? x))
|
(define (jolt-string? x) (string? x))
|
||||||
(define (jolt-char-pred? x) (char? x))
|
(define (jolt-char-pred? x) (char? x))
|
||||||
|
|
|
||||||
|
|
@ -484,6 +484,10 @@
|
||||||
;; reports PersistentList / IPersistentList / IPersistentStack — extend-protocol
|
;; reports PersistentList / IPersistentList / IPersistentStack — extend-protocol
|
||||||
;; clojure.lang.IPersistentList (algo.monads' writer monad) dispatches on one.
|
;; clojure.lang.IPersistentList (algo.monads' writer monad) dispatches on one.
|
||||||
((or (cseq? obj) (empty-list-t? obj)) '("PersistentList" "IPersistentList" "IPersistentStack" "ASeq" "ISeq" "IPersistentCollection" "Sequential" "Collection" "Iterable" "java.lang.Iterable" "Object"))
|
((or (cseq? obj) (empty-list-t? obj)) '("PersistentList" "IPersistentList" "IPersistentStack" "ASeq" "ISeq" "IPersistentCollection" "Sequential" "Collection" "Iterable" "java.lang.Iterable" "Object"))
|
||||||
|
;; a lazy seq (map/filter/… result) is clojure.lang.LazySeq: a Sequential
|
||||||
|
;; ISeq, but not a PersistentList — matching the JVM so extend-protocol /
|
||||||
|
;; instance? on a deferred seq dispatch like an eager one where they should.
|
||||||
|
((jolt-lazyseq? obj) '("LazySeq" "ISeq" "IPersistentCollection" "Sequential" "Collection" "Iterable" "java.lang.Iterable" "Object"))
|
||||||
;; a var is clojure.lang.Var (also IDeref / IFn) — reitit's Expand protocol
|
;; a var is clojure.lang.Var (also IDeref / IFn) — reitit's Expand protocol
|
||||||
;; extends to Var so a #'handler route dispatches.
|
;; extends to Var so a #'handler route dispatches.
|
||||||
((var-cell? obj) '("Var" "clojure.lang.Var" "IDeref" "IFn" "Object"))
|
((var-cell? obj) '("Var" "clojure.lang.Var" "IDeref" "IFn" "Object"))
|
||||||
|
|
|
||||||
|
|
@ -375,7 +375,7 @@
|
||||||
(guard (e (#t #f))
|
(guard (e (#t #f))
|
||||||
(def-var! "clojure.core" "rationalize" (letrec ((rationalize (lambda (x) (let fnrec4398 ((x x)) x)))) rationalize)))
|
(def-var! "clojure.core" "rationalize" (letrec ((rationalize (lambda (x) (let fnrec4398 ((x x)) x)))) rationalize)))
|
||||||
(guard (e (#t #f))
|
(guard (e (#t #f))
|
||||||
(def-var! "clojure.core" "dedupe" (letrec ((dedupe (case-lambda (() (let fnrec4399 () (lambda (rf) (let fnrec4400 ((rf rf)) (let* ((pv (jolt-invoke (var-deref "clojure.core" "volatile!") (let* ((_o$4401 #f) (_o$4402 jolt-nil)) (jolt-vector _o$4401 _o$4402))))) (case-lambda (() (let fnrec4403 () (jolt-invoke rf))) ((result) (let fnrec4404 ((result result)) (jolt-invoke rf result))) ((result input) (let fnrec4405 ((result result) (input input)) (let* ((G__129 (jolt-invoke (var-deref "clojure.core" "deref") pv)) (seen (jolt-nth G__129 0 jolt-nil)) (prior (jolt-nth G__129 1 jolt-nil))) (begin (jolt-invoke (var-deref "clojure.core" "vreset!") pv (let* ((_o$4406 #t) (_o$4407 input)) (jolt-vector _o$4406 _o$4407))) (if (jolt-truthy? (let* ((and__25__auto seen)) (if (jolt-truthy? and__25__auto) (jolt= prior input) and__25__auto))) result (jolt-invoke rf result input)))))))))))) ((coll) (let fnrec4408 ((coll coll)) (let* ((step (letrec ((step (lambda (s prev) (let fnrec4409 ((s s) (prev prev)) (jolt-invoke (var-deref "clojure.core" "make-lazy-seq") (lambda () (let fnrec4410 () (let* ((s (jolt-seq s))) (if (jolt-truthy? s) (let* ((x (jolt-first s))) (if (jolt= x prev) (jolt-invoke (var-deref "clojure.core" "coll->cells") (step (jolt-rest s) prev)) (jolt-invoke (var-deref "clojure.core" "coll->cells") (jolt-cons x (step (jolt-rest s) x))))) jolt-nil))))))))) step))) (let* ((s (jolt-seq coll))) (if (jolt-truthy? s) (jolt-invoke (var-deref "clojure.core" "make-lazy-seq") (lambda () (let fnrec4411 () (jolt-invoke (var-deref "clojure.core" "coll->cells") (let* ((_a$4415 (jolt-first s)) (_a$4416 (let* ((_a$4412 step) (_a$4413 (jolt-rest s)) (_a$4414 (jolt-first s))) (jolt-invoke _a$4412 _a$4413 _a$4414)))) (jolt-cons _a$4415 _a$4416)))))) (jolt-list ))))))))) dedupe)))
|
(def-var! "clojure.core" "dedupe" (letrec ((dedupe (case-lambda (() (let fnrec4399 () (lambda (rf) (let fnrec4400 ((rf rf)) (let* ((pv (jolt-invoke (var-deref "clojure.core" "volatile!") (let* ((_o$4401 #f) (_o$4402 jolt-nil)) (jolt-vector _o$4401 _o$4402))))) (case-lambda (() (let fnrec4403 () (jolt-invoke rf))) ((result) (let fnrec4404 ((result result)) (jolt-invoke rf result))) ((result input) (let fnrec4405 ((result result) (input input)) (let* ((G__129 (jolt-invoke (var-deref "clojure.core" "deref") pv)) (seen (jolt-nth G__129 0 jolt-nil)) (prior (jolt-nth G__129 1 jolt-nil))) (begin (jolt-invoke (var-deref "clojure.core" "vreset!") pv (let* ((_o$4406 #t) (_o$4407 input)) (jolt-vector _o$4406 _o$4407))) (if (jolt-truthy? (let* ((and__25__auto seen)) (if (jolt-truthy? and__25__auto) (jolt= prior input) and__25__auto))) result (jolt-invoke rf result input)))))))))))) ((coll) (let fnrec4408 ((coll coll)) (let* ((step (letrec ((step (lambda (s prev) (let fnrec4409 ((s s) (prev prev)) (jolt-invoke (var-deref "clojure.core" "make-lazy-seq") (lambda () (let fnrec4410 () (let* ((s (jolt-seq s))) (if (jolt-truthy? s) (let* ((x (jolt-first s))) (if (jolt= x prev) (jolt-invoke (var-deref "clojure.core" "coll->cells") (step (jolt-rest s) prev)) (jolt-invoke (var-deref "clojure.core" "coll->cells") (jolt-cons x (step (jolt-rest s) x))))) jolt-nil))))))))) step))) (jolt-invoke (var-deref "clojure.core" "make-lazy-seq") (lambda () (let fnrec4411 () (let* ((s (jolt-seq coll))) (if (jolt-truthy? s) (jolt-invoke (var-deref "clojure.core" "coll->cells") (let* ((_a$4415 (jolt-first s)) (_a$4416 (let* ((_a$4412 step) (_a$4413 (jolt-rest s)) (_a$4414 (jolt-first s))) (jolt-invoke _a$4412 _a$4413 _a$4414)))) (jolt-cons _a$4415 _a$4416))) jolt-nil))))))))))) dedupe)))
|
||||||
(guard (e (#t #f))
|
(guard (e (#t #f))
|
||||||
(def-var! "clojure.core" "seq-to-map-for-destructuring" (letrec ((seq-to-map-for-destructuring (lambda (s) (let fnrec4417 ((s s)) (if (jolt-truthy? (jolt-next s)) (let* ((m (jolt-hash-map)) (xs (jolt-seq s))) (let loop4418 ((m m) (xs xs)) (if (jolt-truthy? xs) (if (jolt-truthy? (jolt-next xs)) (let* ((_a$4422 (let* ((_a$4419 m) (_a$4420 (jolt-first xs)) (_a$4421 (jolt-invoke (var-deref "clojure.core" "second") xs))) (jolt-assoc _a$4419 _a$4420 _a$4421))) (_a$4423 (jolt-invoke (var-deref "clojure.core" "nnext") xs))) (loop4418 _a$4422 _a$4423)) (jolt-throw (jolt-invoke (var-deref "clojure.core" "str") "No value supplied for key: " (jolt-first xs)))) m))) (if (jolt-truthy? (jolt-seq s)) (jolt-first s) (jolt-hash-map))))))) seq-to-map-for-destructuring)))
|
(def-var! "clojure.core" "seq-to-map-for-destructuring" (letrec ((seq-to-map-for-destructuring (lambda (s) (let fnrec4417 ((s s)) (if (jolt-truthy? (jolt-next s)) (let* ((m (jolt-hash-map)) (xs (jolt-seq s))) (let loop4418 ((m m) (xs xs)) (if (jolt-truthy? xs) (if (jolt-truthy? (jolt-next xs)) (let* ((_a$4422 (let* ((_a$4419 m) (_a$4420 (jolt-first xs)) (_a$4421 (jolt-invoke (var-deref "clojure.core" "second") xs))) (jolt-assoc _a$4419 _a$4420 _a$4421))) (_a$4423 (jolt-invoke (var-deref "clojure.core" "nnext") xs))) (loop4418 _a$4422 _a$4423)) (jolt-throw (jolt-invoke (var-deref "clojure.core" "str") "No value supplied for key: " (jolt-first xs)))) m))) (if (jolt-truthy? (jolt-seq s)) (jolt-first s) (jolt-hash-map))))))) seq-to-map-for-destructuring)))
|
||||||
(guard (e (#t #f))
|
(guard (e (#t #f))
|
||||||
|
|
|
||||||
|
|
@ -250,10 +250,16 @@
|
||||||
(if (any-nil? seqs) jolt-empty-list
|
(if (any-nil? seqs) jolt-empty-list
|
||||||
(cseq-lazy (apply jolt-invoke f (map seq-first seqs))
|
(cseq-lazy (apply jolt-invoke f (map seq-first seqs))
|
||||||
(lambda () (map-seq* f (map (lambda (s) (jolt-seq (seq-more s))) seqs))))))
|
(lambda () (map-seq* f (map (lambda (s) (jolt-seq (seq-more s))) seqs))))))
|
||||||
|
;; map is fully lazy: Clojure's (map f coll) is a LazySeq whose body — including
|
||||||
|
;; (f (first coll)) — runs only when forced, so a side-effecting f does not fire
|
||||||
|
;; at construction. Wrap the (eager-headed) map-seq in a lazy-seq node; forcing it
|
||||||
|
;; once yields the cseq chain, which then iterates with no per-element overhead.
|
||||||
|
;; jolt-seq coerces map-seq's result (cseq | jolt-empty-list) to cseq | nil, the
|
||||||
|
;; contract force-lazyseq relies on (see jolt-rest).
|
||||||
(define (jolt-map f . colls)
|
(define (jolt-map f . colls)
|
||||||
(if (null? (cdr colls))
|
(if (null? (cdr colls))
|
||||||
(map-seq f (jolt-seq (car colls)))
|
(jolt-make-lazy-seq (lambda () (jolt-seq (map-seq f (jolt-seq (car colls))))))
|
||||||
(map-seq* f (map jolt-seq colls))))
|
(jolt-make-lazy-seq (lambda () (jolt-seq (map-seq* f (map jolt-seq colls)))))))
|
||||||
|
|
||||||
(define (filter-seq pred s keep)
|
(define (filter-seq pred s keep)
|
||||||
(let loop ((s s))
|
(let loop ((s s))
|
||||||
|
|
@ -261,8 +267,12 @@
|
||||||
((eq? keep (jolt-truthy? (jolt-invoke pred (seq-first s))))
|
((eq? keep (jolt-truthy? (jolt-invoke pred (seq-first s))))
|
||||||
(cseq-lazy (seq-first s) (lambda () (filter-seq pred (jolt-seq (seq-more s)) keep))))
|
(cseq-lazy (seq-first s) (lambda () (filter-seq pred (jolt-seq (seq-more s)) keep))))
|
||||||
(else (loop (jolt-seq (seq-more s)))))))
|
(else (loop (jolt-seq (seq-more s)))))))
|
||||||
(define (jolt-filter pred coll) (filter-seq pred (jolt-seq coll) #t))
|
;; filter/remove are fully lazy (LazySeq): defer the predicate and the source seq
|
||||||
(define (jolt-remove pred coll) (filter-seq pred (jolt-seq coll) #f))
|
;; until forced, like Clojure. (lazy-seq* = a 0-arg lazy node coercing to cseq|nil.)
|
||||||
|
(define (jolt-filter pred coll)
|
||||||
|
(jolt-make-lazy-seq (lambda () (jolt-seq (filter-seq pred (jolt-seq coll) #t)))))
|
||||||
|
(define (jolt-remove pred coll)
|
||||||
|
(jolt-make-lazy-seq (lambda () (jolt-seq (filter-seq pred (jolt-seq coll) #f)))))
|
||||||
|
|
||||||
;; honors `reduced`: a reducing fn that returns (reduced x) stops the fold and
|
;; honors `reduced`: a reducing fn that returns (reduced x) stops the fold and
|
||||||
;; unwraps to x (so does a reduced INIT). Checked at entry, so the value returned
|
;; unwraps to x (so does a reduced INIT). Checked at entry, so the value returned
|
||||||
|
|
@ -332,9 +342,13 @@
|
||||||
;; (take 0 (rest s)) never seqs coll. Realizing one more, as forcing seq-more at
|
;; (take 0 (rest s)) never seqs coll. Realizing one more, as forcing seq-more at
|
||||||
;; the boundary would, over-runs the source by one (medley's sequence-padded).
|
;; the boundary would, over-runs the source by one (medley's sequence-padded).
|
||||||
(define (jolt-take n coll)
|
(define (jolt-take n coll)
|
||||||
;; (take Double/POSITIVE_INFINITY coll) takes the whole coll on the JVM (the
|
;; lazy (LazySeq): realize exactly n elements, none at construction. (take
|
||||||
;; count never reaches 0); test.check's rose-tree unchunk relies on it. Coercing
|
;; Double/POSITIVE_INFINITY coll) takes the whole coll on the JVM (the count
|
||||||
;; +inf.0 to a fixnum index would throw, so take all up front.
|
;; never reaches 0); test.check's rose-tree unchunk relies on it. Coercing +inf.0
|
||||||
|
;; to a fixnum index would throw, so take all up front in that case.
|
||||||
|
(jolt-make-lazy-seq
|
||||||
|
(lambda ()
|
||||||
|
(jolt-seq
|
||||||
(if (and (flonum? n) (infinite? n))
|
(if (and (flonum? n) (infinite? n))
|
||||||
(if (> n 0.0) (jolt-seq coll) jolt-empty-list)
|
(if (> n 0.0) (jolt-seq coll) jolt-empty-list)
|
||||||
(let ((n (->idx n)))
|
(let ((n (->idx n)))
|
||||||
|
|
@ -342,11 +356,14 @@
|
||||||
(cond
|
(cond
|
||||||
((or (fx<=? n 0) (jolt-nil? s)) jolt-empty-list)
|
((or (fx<=? n 0) (jolt-nil? s)) jolt-empty-list)
|
||||||
((fx=? n 1) (cseq-lazy (seq-first s) (lambda () jolt-empty-list)))
|
((fx=? n 1) (cseq-lazy (seq-first s) (lambda () jolt-empty-list)))
|
||||||
(else (cseq-lazy (seq-first s) (lambda () (loop (fx- n 1) (jolt-seq (seq-more s)))))))))))
|
(else (cseq-lazy (seq-first s) (lambda () (loop (fx- n 1) (jolt-seq (seq-more s))))))))))))))
|
||||||
(define (jolt-drop n coll)
|
(define (jolt-drop n coll)
|
||||||
|
(jolt-make-lazy-seq
|
||||||
|
(lambda ()
|
||||||
|
(jolt-seq
|
||||||
(let loop ((n (->idx n)) (s (jolt-seq coll)))
|
(let loop ((n (->idx n)) (s (jolt-seq coll)))
|
||||||
(if (or (fx<=? n 0) (jolt-nil? s)) (if (jolt-nil? s) jolt-empty-list s)
|
(if (or (fx<=? n 0) (jolt-nil? s)) (if (jolt-nil? s) jolt-empty-list s)
|
||||||
(loop (fx- n 1) (jolt-seq (seq-more s))))))
|
(loop (fx- n 1) (jolt-seq (seq-more s)))))))))
|
||||||
|
|
||||||
;; lazily append seq a then the seqable produced by the thunk `brest` — the rest
|
;; lazily append seq a then the seqable produced by the thunk `brest` — the rest
|
||||||
;; is NOT forced until a is exhausted, so concat is fully lazy (Clojure semantics).
|
;; is NOT forced until a is exhausted, so concat is fully lazy (Clojure semantics).
|
||||||
|
|
@ -357,9 +374,12 @@
|
||||||
(if (jolt-nil? a) (jolt-seq (brest))
|
(if (jolt-nil? a) (jolt-seq (brest))
|
||||||
(cseq-lazy (seq-first a) (lambda () (concat2 (jolt-seq (seq-more a)) brest)))))
|
(cseq-lazy (seq-first a) (lambda () (concat2 (jolt-seq (seq-more a)) brest)))))
|
||||||
(define (jolt-concat . colls)
|
(define (jolt-concat . colls)
|
||||||
|
(jolt-make-lazy-seq
|
||||||
|
(lambda ()
|
||||||
|
(jolt-seq
|
||||||
(cond ((null? colls) jolt-empty-list)
|
(cond ((null? colls) jolt-empty-list)
|
||||||
((null? (cdr colls)) (jolt-seq (car colls)))
|
((null? (cdr colls)) (jolt-seq (car colls)))
|
||||||
(else (concat2 (jolt-seq (car colls)) (lambda () (apply jolt-concat (cdr colls)))))))
|
(else (concat2 (jolt-seq (car colls)) (lambda () (apply jolt-concat (cdr colls))))))))))
|
||||||
|
|
||||||
;; Lazily concatenate a (possibly infinite) SEQ of colls — what (apply concat ss)
|
;; Lazily concatenate a (possibly infinite) SEQ of colls — what (apply concat ss)
|
||||||
;; means, but without realizing ss. Pulls one coll at a time, concatenating it with
|
;; means, but without realizing ss. Pulls one coll at a time, concatenating it with
|
||||||
|
|
|
||||||
|
|
@ -160,11 +160,14 @@
|
||||||
(coll->cells (step (rest s) prev))
|
(coll->cells (step (rest s) prev))
|
||||||
(coll->cells (cons x (step (rest s) x)))))
|
(coll->cells (cons x (step (rest s) x)))))
|
||||||
nil)))))]
|
nil)))))]
|
||||||
|
;; defer (seq coll) into the lazy-seq so a side-effecting source is not
|
||||||
|
;; realized at construction (dedupe is lazy, like Clojure's).
|
||||||
|
(make-lazy-seq
|
||||||
|
(fn* []
|
||||||
(let [s (seq coll)]
|
(let [s (seq coll)]
|
||||||
(if s
|
(if s
|
||||||
(make-lazy-seq
|
(coll->cells (cons (first s) (step (rest s) (first s))))
|
||||||
(fn* [] (coll->cells (cons (first s) (step (rest s) (first s))))))
|
nil)))))))
|
||||||
())))))
|
|
||||||
|
|
||||||
;; Internal helper for {:keys [...]} destructuring over a seq of k/v pairs —
|
;; Internal helper for {:keys [...]} destructuring over a seq of k/v pairs —
|
||||||
;; canonical Clojure 1.11 shape (core.clj seq-to-map-for-destructuring):
|
;; canonical Clojure 1.11 shape (core.clj seq-to-map-for-destructuring):
|
||||||
|
|
|
||||||
|
|
@ -766,6 +766,15 @@
|
||||||
{:suite "lazy / construction & laziness" :label "not eagerly evaluated" :expected "0" :actual "(let [c (atom 0)] (lazy-seq (swap! c inc) nil) @c)"}
|
{:suite "lazy / construction & laziness" :label "not eagerly evaluated" :expected "0" :actual "(let [c (atom 0)] (lazy-seq (swap! c inc) nil) @c)"}
|
||||||
{:suite "lazy / construction & laziness" :label "realized on demand" :expected "1" :actual "(let [c (atom 0) s (lazy-seq (swap! c inc) [1])] (first s) @c)"}
|
{:suite "lazy / construction & laziness" :label "realized on demand" :expected "1" :actual "(let [c (atom 0) s (lazy-seq (swap! c inc) [1])] (first s) @c)"}
|
||||||
{:suite "lazy / construction & laziness" :label "lazy-cat" :expected "[0 1 2 3]" :actual "(lazy-cat [0 1] [2 3])"}
|
{:suite "lazy / construction & laziness" :label "lazy-cat" :expected "[0 1 2 3]" :actual "(lazy-cat [0 1] [2 3])"}
|
||||||
|
{:suite "lazy / seq fns are lazy (no side effect at construction)" :label "map" :expected "0" :actual "(let [a (atom 0)] (map (fn [x] (swap! a inc) x) (range 5)) @a)"}
|
||||||
|
{:suite "lazy / seq fns are lazy (no side effect at construction)" :label "filter" :expected "0" :actual "(let [a (atom 0)] (filter (fn [x] (swap! a inc) true) (range 5)) @a)"}
|
||||||
|
{:suite "lazy / seq fns are lazy (no side effect at construction)" :label "remove" :expected "0" :actual "(let [a (atom 0)] (remove (fn [x] (swap! a inc) false) (range 5)) @a)"}
|
||||||
|
{:suite "lazy / seq fns are lazy (no side effect at construction)" :label "take over a lazy source" :expected "0" :actual "(let [a (atom 0)] (take 3 (map (fn [x] (swap! a inc) x) (range 100))) @a)"}
|
||||||
|
{:suite "lazy / seq fns are lazy (no side effect at construction)" :label "drop over a lazy source" :expected "0" :actual "(let [a (atom 0)] (drop 2 (map (fn [x] (swap! a inc) x) (range 100))) @a)"}
|
||||||
|
{:suite "lazy / seq fns are lazy (no side effect at construction)" :label "concat over lazy sources" :expected "0" :actual "(let [a (atom 0)] (concat (map (fn [x] (swap! a inc) x) [1 2]) (map (fn [x] (swap! a inc) x) [3 4])) @a)"}
|
||||||
|
{:suite "lazy / seq fns are lazy (no side effect at construction)" :label "take-while" :expected "0" :actual "(let [a (atom 0)] (take-while (fn [x] (swap! a inc) true) (range 5)) @a)"}
|
||||||
|
{:suite "lazy / seq fns are lazy (no side effect at construction)" :label "partition over a lazy source" :expected "0" :actual "(let [a (atom 0)] (partition 2 (map (fn [x] (swap! a inc) x) (range 6))) @a)"}
|
||||||
|
{:suite "lazy / result type is LazySeq" :label "map/filter/take/concat/mapcat are LazySeq" :expected "[true true true true true]" :actual "(mapv #(instance? clojure.lang.LazySeq %) [(map inc [1]) (filter odd? [1]) (take 1 [1]) (concat [1]) (mapcat list [1])])"}
|
||||||
{:suite "lazy / construction & laziness" :label "doall forces" :expected "[2 3 4]" :actual "(doall (map inc [1 2 3]))"}
|
{:suite "lazy / construction & laziness" :label "doall forces" :expected "[2 3 4]" :actual "(doall (map inc [1 2 3]))"}
|
||||||
{:suite "lazy / construction & laziness" :label "dorun returns nil" :expected "nil" :actual "(dorun (map inc [1 2 3]))"}
|
{:suite "lazy / construction & laziness" :label "dorun returns nil" :expected "nil" :actual "(dorun (map inc [1 2 3]))"}
|
||||||
{:suite "lazy / infinite" :label "take from repeat" :expected "[7 7 7]" :actual "(take 3 (repeat 7))"}
|
{:suite "lazy / infinite" :label "take from repeat" :expected "[7 7 7]" :actual "(take 3 (repeat 7))"}
|
||||||
|
|
|
||||||
|
|
@ -475,10 +475,10 @@
|
||||||
{:suite "type" :expr "(type '(1 2))" :expected "clojure.lang.PersistentList"}
|
{:suite "type" :expr "(type '(1 2))" :expected "clojure.lang.PersistentList"}
|
||||||
{:suite "type" :expr "(type '())" :expected "clojure.lang.PersistentList$EmptyList"}
|
{:suite "type" :expr "(type '())" :expected "clojure.lang.PersistentList$EmptyList"}
|
||||||
{:suite "type" :expr "(type (first {:a 1}))" :expected "clojure.lang.PersistentVector"}
|
{:suite "type" :expr "(type (first {:a 1}))" :expected "clojure.lang.PersistentVector"}
|
||||||
{:suite "type" :expr "(type (map inc [1 2]))" :expected "clojure.lang.PersistentList"}
|
{:suite "type" :expr "(type (map inc [1 2]))" :expected "clojure.lang.LazySeq"}
|
||||||
{:suite "type" :expr "(type (filter odd? [1 2 3]))" :expected "clojure.lang.PersistentList"}
|
{:suite "type" :expr "(type (filter odd? [1 2 3]))" :expected "clojure.lang.LazySeq"}
|
||||||
{:suite "type" :expr "(type (lazy-seq (cons 1 nil)))" :expected "clojure.lang.LazySeq"}
|
{:suite "type" :expr "(type (lazy-seq (cons 1 nil)))" :expected "clojure.lang.LazySeq"}
|
||||||
{:suite "type" :expr "(type (take 2 (iterate inc 0)))" :expected "clojure.lang.PersistentList"}
|
{:suite "type" :expr "(type (take 2 (iterate inc 0)))" :expected "clojure.lang.LazySeq"}
|
||||||
{:suite "type" :expr "(type inc)" :expected "clojure.core$inc"}
|
{:suite "type" :expr "(type inc)" :expected "clojure.core$inc"}
|
||||||
{:suite "type" :expr "(type (sorted-map :a 1))" :expected ":map"}
|
{:suite "type" :expr "(type (sorted-map :a 1))" :expected ":map"}
|
||||||
{:suite "type" :expr "(type (sorted-set 1))" :expected ":jolt/sorted-set"}
|
{:suite "type" :expr "(type (sorted-set 1))" :expected ":jolt/sorted-set"}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue