Merge pull request #265 from jolt-lang/conformance/lazy-map
seq fns are lazy by default (LazySeq), like Clojure
This commit is contained in:
commit
83ff96c3c8
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
|
||||
;; concat/map/cons is a lazy cseq with list?=#f, but evaluating it still means
|
||||
;; 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-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
|
||||
|
|
@ -106,7 +108,7 @@
|
|||
;; list items -> jolt vector (pvec); the analyzer mapv's over the result.
|
||||
(define (hc-elements x)
|
||||
(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)))
|
||||
(define (hc-vec-items x) x) ; already a pvec
|
||||
(define (hc-set-items x)
|
||||
|
|
|
|||
|
|
@ -751,6 +751,7 @@
|
|||
((string=? iface "IPersistentSet") (or (pset? val) (htable-sorted-set? val)))
|
||||
((string=? iface "ISeq")
|
||||
(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
|
||||
;; collection, not just seqs (a vector IS Seqable, not an ISeq).
|
||||
((string=? iface "Seqable")
|
||||
|
|
|
|||
|
|
@ -105,8 +105,9 @@
|
|||
(if (null? colls)
|
||||
(td-mapcat f)
|
||||
;; lazily concat the per-element results — no seq->list, so mapcat over an
|
||||
;; infinite source stays lazy.
|
||||
(lazy-concat-seq (apply jolt-map f colls))))
|
||||
;; infinite source stays lazy; the outer lazy-seq node defers the first
|
||||
;; 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.
|
||||
(define (take-while-seq pred s)
|
||||
|
|
@ -118,7 +119,7 @@
|
|||
(define jolt-take-while
|
||||
(case-lambda
|
||||
((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)
|
||||
(let loop ((s (jolt-seq coll)))
|
||||
(if (and (not (jolt-nil? s)) (jolt-truthy? (jolt-invoke pred (seq-first s))))
|
||||
|
|
@ -127,7 +128,7 @@
|
|||
(define jolt-drop-while
|
||||
(case-lambda
|
||||
((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 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.
|
||||
(define jolt-partition
|
||||
(case-lambda
|
||||
((n coll) (partition* (->idx n) (->idx n) #f #f coll))
|
||||
((n step coll) (partition* (->idx n) (->idx step) #f #f coll))
|
||||
((n step pad coll) (partition* (->idx n) (->idx step) #t pad coll))))
|
||||
((n coll) (jolt-make-lazy-seq (lambda () (jolt-seq (partition* (->idx n) (->idx n) #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) (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)
|
||||
(let loop ((n n) (s s) (acc '()))
|
||||
(if (or (fx<=? n 0) (jolt-nil? s))
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
;; 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-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-string? x) (string? x))
|
||||
(define (jolt-char-pred? x) (char? x))
|
||||
|
|
|
|||
|
|
@ -484,6 +484,10 @@
|
|||
;; reports PersistentList / IPersistentList / IPersistentStack — extend-protocol
|
||||
;; 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"))
|
||||
;; 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
|
||||
;; extends to Var so a #'handler route dispatches.
|
||||
((var-cell? obj) '("Var" "clojure.lang.Var" "IDeref" "IFn" "Object"))
|
||||
|
|
|
|||
|
|
@ -375,7 +375,7 @@
|
|||
(guard (e (#t #f))
|
||||
(def-var! "clojure.core" "rationalize" (letrec ((rationalize (lambda (x) (let fnrec4398 ((x x)) x)))) rationalize)))
|
||||
(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))
|
||||
(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))
|
||||
|
|
|
|||
|
|
@ -261,10 +261,16 @@
|
|||
(if (any-nil? seqs) jolt-empty-list
|
||||
(cseq-lazy (apply jolt-invoke f (map seq-first 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)
|
||||
(if (null? (cdr colls))
|
||||
(map-seq f (jolt-seq (car colls)))
|
||||
(map-seq* f (map jolt-seq colls))))
|
||||
(jolt-make-lazy-seq (lambda () (jolt-seq (map-seq f (jolt-seq (car colls))))))
|
||||
(jolt-make-lazy-seq (lambda () (jolt-seq (map-seq* f (map jolt-seq colls)))))))
|
||||
|
||||
(define (filter-seq pred s keep)
|
||||
(let loop ((s s))
|
||||
|
|
@ -272,8 +278,12 @@
|
|||
((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))))
|
||||
(else (loop (jolt-seq (seq-more s)))))))
|
||||
(define (jolt-filter pred coll) (filter-seq pred (jolt-seq coll) #t))
|
||||
(define (jolt-remove pred coll) (filter-seq pred (jolt-seq coll) #f))
|
||||
;; filter/remove are fully lazy (LazySeq): defer the predicate and the source seq
|
||||
;; 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
|
||||
;; unwraps to x (so does a reduced INIT). Checked at entry, so the value returned
|
||||
|
|
@ -343,21 +353,28 @@
|
|||
;; (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).
|
||||
(define (jolt-take n coll)
|
||||
;; (take Double/POSITIVE_INFINITY coll) takes the whole coll on the JVM (the
|
||||
;; count 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.
|
||||
(if (and (flonum? n) (infinite? n))
|
||||
(if (> n 0.0) (jolt-seq coll) jolt-empty-list)
|
||||
(let ((n (->idx n)))
|
||||
(let loop ((n n) (s (jolt-seq coll)))
|
||||
(cond
|
||||
((or (fx<=? n 0) (jolt-nil? s)) 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)))))))))))
|
||||
;; lazy (LazySeq): realize exactly n elements, none at construction. (take
|
||||
;; Double/POSITIVE_INFINITY coll) takes the whole coll on the JVM (the count
|
||||
;; 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 (> n 0.0) (jolt-seq coll) jolt-empty-list)
|
||||
(let ((n (->idx n)))
|
||||
(let loop ((n n) (s (jolt-seq coll)))
|
||||
(cond
|
||||
((or (fx<=? n 0) (jolt-nil? s)) 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))))))))))))))
|
||||
(define (jolt-drop n 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)
|
||||
(loop (fx- n 1) (jolt-seq (seq-more s))))))
|
||||
(jolt-make-lazy-seq
|
||||
(lambda ()
|
||||
(jolt-seq
|
||||
(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)
|
||||
(loop (fx- n 1) (jolt-seq (seq-more s)))))))))
|
||||
|
||||
;; 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).
|
||||
|
|
@ -368,9 +385,12 @@
|
|||
(if (jolt-nil? a) (jolt-seq (brest))
|
||||
(cseq-lazy (seq-first a) (lambda () (concat2 (jolt-seq (seq-more a)) brest)))))
|
||||
(define (jolt-concat . colls)
|
||||
(cond ((null? colls) jolt-empty-list)
|
||||
((null? (cdr colls)) (jolt-seq (car colls)))
|
||||
(else (concat2 (jolt-seq (car colls)) (lambda () (apply jolt-concat (cdr colls)))))))
|
||||
(jolt-make-lazy-seq
|
||||
(lambda ()
|
||||
(jolt-seq
|
||||
(cond ((null? colls) jolt-empty-list)
|
||||
((null? (cdr colls)) (jolt-seq (car 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)
|
||||
;; means, but without realizing ss. Pulls one coll at a time, concatenating it with
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue