Chez Phase 1 (increment 3n): seq-native shims + reduced

The dominant prelude-parity crash bucket was 'apply non-procedure jolt-nil':
core fns calling seed-native seq fns (core_coll.janet) that have no Chez RT
shim, so var-deref returns jolt-nil. A static scan of the assembled prelude
turned up 52 referenced-but-undefined clojure.core names.

host/chez/natives-seq.ss shims the safe seq fns over the existing seq layer:
mapcat, take-while, drop-while, partition (collection arities only — the 1-arg
transducer forms are jolt-kxsr), and sort (compare default; a comparator may
return a 3-way number or a boolean less-than). reduced/reduced? is a jolt-reduced
record in seq.ss that reduce short-circuits on and deref unwraps, so unreduced
works. identical? = jolt= (the seed's definition).

Deferred list?: a Chez lazy seq and a list are both cseq, so it can't be told
apart without a distinct list type — a real divergence risk.

Parity 1407 -> 1467/2497, 0 new divergences. emit-test 263/263.
This commit is contained in:
Yogthos 2026-06-17 23:16:26 -04:00
parent c28b5406ca
commit 739b219d0e
7 changed files with 183 additions and 11 deletions

View file

@ -17,10 +17,13 @@
;; (watches/validators are overlay features layered via jolt.host/ref-put!).
(define (jolt-atom-new v . _opts) (make-jolt-atom v))
;; deref reads an atom; it also unwraps a `reduced` (Clojure @(reduced x) => x,
;; which the overlay's `unreduced` relies on). The reduced record is in seq.ss.
(define (jolt-deref x)
(if (jolt-atom? x)
(jolt-atom-val x)
(error #f "deref: unsupported reference type" x)))
(cond
((jolt-atom? x) (jolt-atom-val x))
((jolt-reduced? x) (jolt-reduced-val x))
(else (error #f "deref: unsupported reference type" x))))
;; (swap! a f arg*) -> (reset! a (f @a arg*)); f is invoked through jolt-invoke
;; (a jolt fn value, keyword, or invokable collection).

99
host/chez/natives-seq.ss Normal file
View file

@ -0,0 +1,99 @@
;; seq-native shims (jolt-y6mv) — seed-native seq fns the overlay assumes are
;; clojure.core natives but which live in the Janet seed (src/jolt/core_coll.janet),
;; so they have no def-var! in the assembled prelude and resolve to jolt-nil on
;; Chez. This was the dominant prelude-parity crash bucket ('apply jolt-nil').
;; Each is a pure fn over the existing seq layer (seq.ss) — collection arities
;; only; the 1-arg transducer arities are jolt-kxsr. Loaded last (after
;; converters.ss for jolt-compare and seq.ss for the reduced record).
;; reduced / reduced? — the box itself is the jolt-reduced record from seq.ss
;; (so the reduce machinery there can see it); these just expose the constructor
;; and predicate. (deref a-reduced) is handled in atoms.ss.
(define (jolt-reduced-new x) (make-jolt-reduced x))
(define (jolt-reduced-pred x) (jolt-reduced? x))
;; mapcat: (mapcat f coll & colls) — map f across the colls (stops at shortest),
;; then concat the results. Collection arity only.
(define (jolt-mapcat f . colls)
(apply jolt-concat (seq->list (apply jolt-map f colls))))
;; take-while / drop-while over the seq layer.
(define (take-while-seq pred s)
(if (jolt-nil? s) jolt-empty-list
(let ((x (seq-first s)))
(if (jolt-truthy? (jolt-invoke pred x))
(cseq-lazy x (lambda () (take-while-seq pred (jolt-seq (seq-more s)))))
jolt-empty-list))))
(define (jolt-take-while pred coll) (take-while-seq pred (jolt-seq coll)))
(define (jolt-drop-while pred coll)
(let loop ((s (jolt-seq coll)))
(if (and (not (jolt-nil? s)) (jolt-truthy? (jolt-invoke pred (seq-first s))))
(loop (jolt-seq (seq-more s)))
(if (jolt-nil? s) jolt-empty-list s))))
;; partition: (partition n coll), (partition n step coll), or
;; (partition n step pad coll). Only complete partitions of size n are kept;
;; with pad, a short final partition is padded from pad (and may be < n if pad
;; 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))))
(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))
(values (reverse acc) s (length acc))
(loop (fx- n 1) (jolt-seq (seq-more s)) (cons (seq-first s) acc)))))
(define (partition* n step has-pad pad coll)
(let loop ((s (jolt-seq coll)))
(if (jolt-nil? s) jolt-empty-list
(let-values (((part rest taken) (take-n n s)))
(cond
;; full partition: emit it, advance `step` from its START
((fx=? taken n)
(cseq-lazy (list->cseq part)
(lambda () (loop (jolt-seq (advance-by step s))))))
;; short final partition with pad: top up to n from pad, then stop
((and has-pad (fx>? taken 0))
(let ((padded (append part (take-list (- n taken) (jolt-seq pad)))))
(cseq-lazy (list->cseq padded) (lambda () jolt-empty-list))))
;; short final partition, no pad: dropped (Clojure keeps only full ones)
(else jolt-empty-list))))))
(define (advance-by step s) ; drop `step` elements from s (seq), returns a seq
(let loop ((step step) (s s))
(if (or (fx<=? step 0) (jolt-nil? s)) s
(loop (fx- step 1) (jolt-seq (seq-more s))))))
(define (take-list n s) ; up to n elements of seq s as a Scheme list
(let loop ((n n) (s s) (acc '()))
(if (or (fx<=? n 0) (jolt-nil? s)) (reverse acc)
(loop (fx- n 1) (jolt-seq (seq-more s)) (cons (seq-first s) acc)))))
;; sort: (sort coll) uses compare; (sort cmp coll) uses cmp, whose result may be
;; a 3-way number (<0 / 0 / >0) OR a boolean (a Clojure-style less-than pred).
(define (cmp->less cmp)
(lambda (a b)
(let ((r (jolt-invoke cmp a b)))
(if (number? r) (< r 0) (jolt-truthy? r)))))
(define jolt-sort
(case-lambda
((coll) (jolt-sort* (cmp->less jolt-compare) coll))
((cmp coll) (jolt-sort* (cmp->less cmp) coll))))
(define (jolt-sort* less? coll)
(let ((s (jolt-seq coll)))
(if (jolt-nil? s) jolt-empty-list
(list->cseq (list-sort less? (seq->list s))))))
;; identical?: jolt reference identity. The seed defines it as (= a b) over its
;; value model (core_types.janet core-identical?), where interned keywords/small
;; values compare equal — so jolt= is the faithful port.
(define (jolt-identical? a b) (jolt= a b))
(def-var! "clojure.core" "reduced" jolt-reduced-new)
(def-var! "clojure.core" "reduced?" jolt-reduced-pred)
(def-var! "clojure.core" "mapcat" jolt-mapcat)
(def-var! "clojure.core" "take-while" jolt-take-while)
(def-var! "clojure.core" "drop-while" jolt-drop-while)
(def-var! "clojure.core" "partition" jolt-partition)
(def-var! "clojure.core" "sort" jolt-sort)
(def-var! "clojure.core" "identical?" jolt-identical?)

View file

@ -160,3 +160,8 @@
;; extends get/count/contains? to see through a transient. After collections.ss
;; (the persistent ops it delegates to).
(load "host/chez/transients.ss")
;; seq-native shims (jolt-y6mv): mapcat/take-while/drop-while/partition/sort +
;; reduced/reduced?/identical? — seed-native fns the overlay assumes are core
;; natives. Over the seq layer + jolt-compare, so loaded after converters.ss.
(load "host/chez/natives-seq.ss")

View file

@ -30,6 +30,11 @@
(define-record-type empty-list-t (fields) (nongenerative empty-list-v1))
(define jolt-empty-list (make-empty-list-t))
;; reduced (jolt-y6mv): a box a reducing fn returns to stop reduce early. The
;; reduce machinery below unwraps it; (deref a-reduced) / unreduced also read it.
;; reduced?/reduced are def-var!'d into clojure.core in natives-seq.ss.
(define-record-type jolt-reduced (fields val) (nongenerative jolt-reduced-v1))
;; ============================================================================
;; jolt-seq — coerce a seqable to a non-empty seq, or jolt-nil when empty
;; ============================================================================
@ -132,8 +137,14 @@
(define (jolt-filter pred coll) (filter-seq pred (jolt-seq coll) #t))
(define (jolt-remove pred coll) (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
;; by the last step is unwrapped on the next turn before the seq is consulted.
(define (reduce-seq f acc s)
(if (jolt-nil? s) acc (reduce-seq f (jolt-invoke f acc (seq-first s)) (jolt-seq (seq-more s)))))
(cond
((jolt-reduced? acc) (jolt-reduced-val acc))
((jolt-nil? s) acc)
(else (reduce-seq f (jolt-invoke f acc (seq-first s)) (jolt-seq (seq-more s))))))
(define jolt-reduce
(case-lambda
((f coll) (let ((s (jolt-seq coll)))