Chez Phase 2 (inc G): lazy-seq bridge (make-lazy-seq / coll->cells)
The lazy-seq macro expands to (make-lazy-seq (fn* [] (coll->cells body))) and lazy-cat to (concat (lazy-seq c) ...); both seed natives were nil on the prelude, so every overlay fn built on lazy-seq — repeat/iterate/ cycle/dedupe/take-nth/keep/interpose/reductions/map-indexed/distinct/ interleave/tree-seq(->flatten)/partition-all/lazy-cat — hit apply-jolt-nil. lazy-bridge.ss bridges to the cseq model: a jolt-lazyseq is a deferred seq forced once by an extended jolt-seq; jolt-cons defers a lazyseq tail so an infinite (repeat/iterate/cycle) stays lazy. A lazyseq is a new value type, so the dispatchers that don't route through jolt-seq learn it (sequential? for =/hash, plus count/empty?/nth/printers) or a raw unrealized lazyseq escapes — the corpus compares (= [1 3 5] (take-nth …)) against it directly. seq.ss: jolt-concat is now fully lazy (the rest isn't forced until the first coll is exhausted), so a self-referential lazy-cat — fib = (lazy-cat [0 1] (map + (rest fib) fib)) — no longer memoizes its tail as empty by reading fib before its def binds. Prelude parity 1837 -> 1886, 0 new divergences. Floor raised to 1886.
This commit is contained in:
parent
241095977f
commit
e434a7d352
4 changed files with 100 additions and 7 deletions
78
host/chez/lazy-bridge.ss
Normal file
78
host/chez/lazy-bridge.ss
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
;; lazy-seq bridge (jolt-cf1q.3, jolt-dmw9) — make-lazy-seq / coll->cells.
|
||||
;;
|
||||
;; The `lazy-seq` macro (00-syntax.clj) expands to
|
||||
;; (make-lazy-seq (fn* [] (coll->cells (do body))))
|
||||
;; and `lazy-cat` to (concat (lazy-seq c) ...). make-lazy-seq / coll->cells are
|
||||
;; seed natives (src/jolt/lazyseq.janet) with no Chez shim, so EVERY overlay fn
|
||||
;; built on lazy-seq — repeat / iterate / cycle / dedupe / take-nth / keep /
|
||||
;; interpose / reductions / tree-seq (-> flatten) / lazy-cat — resolved the call
|
||||
;; to jolt-nil and hit the apply-jolt-nil crash bucket.
|
||||
;;
|
||||
;; Bridge to the cseq model (seq.ss): a `jolt-lazyseq` is a deferred seq — a 0-arg
|
||||
;; thunk that, when forced once, yields a seq (cseq | nil). coll->cells coerces the
|
||||
;; body result to a seq (= jolt-seq), so the thunk already returns a seq; jolt-seq
|
||||
;; is extended to force a lazyseq. The one trap: (cons x (a-lazy-seq)) must NOT
|
||||
;; force the tail (else (repeat x) = (lazy-seq (cons x (repeat x))) loops forever),
|
||||
;; so jolt-cons defers a lazyseq tail into a lazy cseq cell.
|
||||
;;
|
||||
;; Loaded LAST (after host-table.ss): %ls-seq then captures the fully-extended
|
||||
;; jolt-seq (sorted-aware), so a lazy body returning a sorted coll still seqs.
|
||||
|
||||
(define-record-type jolt-lazyseq
|
||||
(fields (mutable thunk) (mutable val) (mutable realized?))
|
||||
(nongenerative jolt-lazyseq-v1))
|
||||
|
||||
(define (jolt-make-lazy-seq thunk) (make-jolt-lazyseq thunk jolt-nil #f))
|
||||
|
||||
;; force once and memoize. The thunk is (fn [] (coll->cells body)); coll->cells
|
||||
;; already coerced the body to a seq (cseq | nil) via the live jolt-seq, so the
|
||||
;; result needs no further coercion (a nested lazyseq was forced by coll->cells).
|
||||
(define (force-lazyseq x)
|
||||
(if (jolt-lazyseq-realized? x)
|
||||
(jolt-lazyseq-val x)
|
||||
(let ((r (jolt-invoke (jolt-lazyseq-thunk x))))
|
||||
(jolt-lazyseq-val-set! x r)
|
||||
(jolt-lazyseq-realized?-set! x #t)
|
||||
(jolt-lazyseq-thunk-set! x #f)
|
||||
r)))
|
||||
|
||||
;; coll->cells: coerce the body result to the cell representation = a seq | nil.
|
||||
(define (jolt-coll->cells c) (jolt-seq c))
|
||||
|
||||
;; extend jolt-seq to force a lazyseq (a lazyseq is seqable -> its realized seq).
|
||||
(define %ls-seq jolt-seq)
|
||||
(set! jolt-seq (lambda (x) (if (jolt-lazyseq? x) (force-lazyseq x) (%ls-seq x))))
|
||||
|
||||
;; (cons x lazyseq): keep the tail lazy — force it only when the cseq cell is
|
||||
;; walked, so an infinite (repeat/iterate/cycle) stays productive.
|
||||
(define %ls-cons jolt-cons)
|
||||
(set! jolt-cons (lambda (x coll)
|
||||
(if (jolt-lazyseq? coll)
|
||||
(cseq-lazy x (lambda () (force-lazyseq coll)))
|
||||
(%ls-cons x coll))))
|
||||
|
||||
;; A lazyseq is a NEW value type, so the dispatchers that DON'T route through
|
||||
;; jolt-seq must learn it or a raw (unrealized) lazyseq escapes — e.g. the corpus
|
||||
;; compares (= [1 3 5] (take-nth 2 …)) against the raw lazyseq, and jolt=2 would
|
||||
;; see an unknown type and return false. Recognizing it as sequential is enough
|
||||
;; for equality + hash (seq=? / seq-hash coerce via jolt-seq); count / empty? /
|
||||
;; nth / the printers don't, so coerce those explicitly.
|
||||
(define %ls-sequential? jolt-sequential?)
|
||||
(set! jolt-sequential? (lambda (x) (or (jolt-lazyseq? x) (%ls-sequential? x))))
|
||||
(define %ls-count jolt-count)
|
||||
(set! jolt-count (lambda (x) (if (jolt-lazyseq? x) (%ls-count (jolt-seq x)) (%ls-count x))))
|
||||
(define %ls-empty? jolt-empty?)
|
||||
(set! jolt-empty? (lambda (x) (if (jolt-lazyseq? x) (%ls-empty? (jolt-seq x)) (%ls-empty? x))))
|
||||
(define %ls-nth jolt-nth)
|
||||
(set! jolt-nth (case-lambda
|
||||
((coll i) (if (jolt-lazyseq? coll) (%ls-nth (jolt-seq coll) i) (%ls-nth coll i)))
|
||||
((coll i d) (if (jolt-lazyseq? coll) (%ls-nth (jolt-seq coll) i d) (%ls-nth coll i d)))))
|
||||
(define %ls-pr-str jolt-pr-str)
|
||||
(set! jolt-pr-str (lambda (x) (if (jolt-lazyseq? x) (%ls-pr-str (jolt-seq x)) (%ls-pr-str x))))
|
||||
(define %ls-pr-readable jolt-pr-readable)
|
||||
(set! jolt-pr-readable (lambda (x) (if (jolt-lazyseq? x) (%ls-pr-readable (jolt-seq x)) (%ls-pr-readable x))))
|
||||
(define %ls-str-render-one jolt-str-render-one)
|
||||
(set! jolt-str-render-one (lambda (x) (if (jolt-lazyseq? x) (%ls-str-render-one (jolt-seq x)) (%ls-str-render-one x))))
|
||||
|
||||
(def-var! "clojure.core" "make-lazy-seq" jolt-make-lazy-seq)
|
||||
(def-var! "clojure.core" "coll->cells" jolt-coll->cells)
|
||||
|
|
@ -206,3 +206,9 @@
|
|||
;; through their :ops table). Loaded LAST — wraps the jrec-extended dispatchers
|
||||
;; (records.ss), jolt-disj (transients.ss), and value-host-tags (records.ss).
|
||||
(load "host/chez/host-table.ss")
|
||||
|
||||
;; lazy-seq bridge (jolt-dmw9, Phase 2): make-lazy-seq / coll->cells over the
|
||||
;; cseq model — unblocks every overlay fn built on the lazy-seq macro (repeat/
|
||||
;; iterate/cycle/dedupe/take-nth/keep/interpose/reductions/tree-seq/lazy-cat).
|
||||
;; Loaded LAST so %ls-seq captures the fully-extended (sorted-aware) jolt-seq.
|
||||
(load "host/chez/lazy-bridge.ss")
|
||||
|
|
|
|||
|
|
@ -176,15 +176,18 @@
|
|||
(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))))))
|
||||
|
||||
(define (concat2 a b) ; lazily append seq a then seqable b
|
||||
(if (jolt-nil? a) (jolt-seq b)
|
||||
(cseq-lazy (seq-first a) (lambda () (concat2 (jolt-seq (seq-more a)) b)))))
|
||||
;; 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).
|
||||
;; This matters for a self-referential lazy-cat (fib = (lazy-cat [0 1] (map + (rest
|
||||
;; fib) fib))): forcing the rest eagerly at construction would read fib before its
|
||||
;; def binds, memoizing the tail as empty.
|
||||
(define (concat2 a brest)
|
||||
(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 (let loop ((c (jolt-seq (car colls))) (rest (cdr colls)))
|
||||
(if (null? rest) (if (jolt-nil? c) jolt-empty-list c)
|
||||
(concat2 c (apply jolt-concat rest)))))))
|
||||
(else (concat2 (jolt-seq (car colls)) (lambda () (apply jolt-concat (cdr colls)))))))
|
||||
|
||||
;; (apply f a b ... coll): spread the trailing seqable into the call.
|
||||
(define (jolt-apply f . args)
|
||||
|
|
|
|||
|
|
@ -170,8 +170,14 @@
|
|||
# sorted-map/sorted-set/subseq/rsubseq + sorted equality; unblocks sorted? and
|
||||
# every fn that calls it: empty/ifn?/reversible?/map?/set?/coll?. Also an emit fix
|
||||
# routing a computed call operator ((sorted-map …) k) through jolt-invoke) 1837.
|
||||
# Phase 2 inc G (jolt-dmw9: lazy-seq bridge — make-lazy-seq / coll->cells over the
|
||||
# cseq model + a jolt-lazyseq arm on the non-jolt-seq dispatchers (sequential?/=/
|
||||
# hash/count/empty?/nth/printers); jolt-concat made fully lazy so a self-
|
||||
# referential lazy-cat (fib) stays productive. Unblocks repeat/iterate/cycle/
|
||||
# dedupe/take-nth/keep/interpose/reductions/map-indexed/distinct/interleave/
|
||||
# tree-seq->flatten/partition-all/lazy-cat) 1886.
|
||||
# Strided runs scale down.
|
||||
(def base-floor (scan-number (or (os/getenv "JOLT_CHEZ_PRELUDE_FLOOR") "1837")))
|
||||
(def base-floor (scan-number (or (os/getenv "JOLT_CHEZ_PRELUDE_FLOOR") "1886")))
|
||||
(def floor (if (os/getenv "JOLT_CORPUS_LIMIT") 0 base-floor))
|
||||
(when (or (> (length diverged) 0) (< pass floor))
|
||||
(printf "REGRESSION: pass %d < floor %d or %d new divergence(s)" pass floor (length diverged)))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue