From bbca8bc0de5c04b90dc58f67c7ec501998df68c8 Mon Sep 17 00:00:00 2001 From: Yogthos Date: Tue, 30 Jun 2026 11:10:36 -0400 Subject: [PATCH] Migrate list?/ratio?/rational? to the overlay; narrow jolt.host exposure list?, ratio?, and rational? are the predicate-web members that are genuinely safe to migrate: not extended at runtime, not on the compiler emit/inference path, not reached by the kernel tier. They now live in the overlay (clojure/core/20-coll.clj) built on the jolt.host tower/rep tests, lowering to the same code the native shims did. Removed their native definitions (predicates.ss) and, for ratio?/rational?, the now-redundant post-prelude re-assertions. Also dropped the dead all-flonum overlay ratio?/rational?/decimal? stubs. The rest of the web stays native and is documented as such: map?/set?/ seq?/coll? are extended with sorted/record/lazy arms, decimal? is extended by the optional bigdec module, integer?/float? are on the emit/inference path, vector? is reached by the kernel-tier peek. jolt.host exposure is therefore narrowed to just the tests these three consume (exact?, rational-type?, cseq?, cseq-list?, empty-list?). Numeric probe is byte-identical to pre-migration; list? correct across list/vector/lazy/empty/cons/rest cases. Selfhost fixpoint holds, values/ unit/smoke/corpus green, bench flat within noise. --- host/chez/lazy-bridge.ss | 5 ---- host/chez/post-prelude.ss | 17 ++++++------ host/chez/predicates.ss | 42 ++++++++++++------------------ host/chez/seed/prelude.ss | 6 ++--- jolt-core/clojure/core/20-coll.clj | 20 +++++++++----- 5 files changed, 43 insertions(+), 47 deletions(-) diff --git a/host/chez/lazy-bridge.ss b/host/chez/lazy-bridge.ss index 3e0071c..76e0043 100644 --- a/host/chez/lazy-bridge.ss +++ b/host/chez/lazy-bridge.ss @@ -93,8 +93,3 @@ (def-var! "clojure.core" "make-lazy-seq" jolt-make-lazy-seq) (def-var! "clojure.core" "coll->cells" jolt-coll->cells) - -;; jolt.host/lazyseq? — raw lazy-seq rep test (the other jolt.host type-test -;; primitives are exposed in predicates.ss; this one lives here because -;; jolt-lazyseq? is defined in this file, which loads after predicates.ss). -(def-var! "jolt.host" "lazyseq?" jolt-lazyseq?) diff --git a/host/chez/post-prelude.ss b/host/chez/post-prelude.ss index d21fb20..9316557 100644 --- a/host/chez/post-prelude.ss +++ b/host/chez/post-prelude.ss @@ -78,18 +78,19 @@ (def-var! "clojure.core" "line-seq" (lambda (rdr) (if (reader-jhost? rdr) (chez-line-seq rdr) (jolt-invoke overlay-line-seq rdr))))) -;; JVM-parity numeric tower: the overlay (20-coll.clj) carries an -;; all-flonum number-predicate web with no Ratio concept (ratio? -> false, -;; double? -> not-integer, float? -> double?, rational? -> int?), which -;; misclassifies exact rationals on the Chez tower (e.g. (double? 1/2) -> true). -;; Re-assert the native tower-correct versions (predicates.ss) so they win over -;; the overlay defs. int?/double? alias integer?/float?. == is value-equality. +;; JVM-parity numeric tower. integer?/float? are on the compiler emit/inference +;; path (so they stay native) but the overlay (20-coll.clj) still carries an +;; all-flonum int?/double? (int? -> integer?, double? -> not-integer) that +;; misclassifies exact rationals (e.g. (double? 1/2) -> true). Re-assert the +;; native tower-correct versions so they win over those overlay defs. int?/double? +;; alias integer?/float?. == is value-equality. (ratio?/rational? are now correct +;; in the overlay, built on jolt.host tower tests, so they need no re-assertion.) (def-var! "clojure.core" "integer?" jolt-integer?) (def-var! "clojure.core" "int?" jolt-integer?) (def-var! "clojure.core" "float?" jolt-float?) (def-var! "clojure.core" "double?" jolt-float?) -(def-var! "clojure.core" "ratio?" jolt-ratio?) -(def-var! "clojure.core" "rational?" jolt-rational?) +;; ratio?/rational? now live (correctly) in the overlay, so they no longer need a +;; native re-assertion here. decimal? stays (bigdec re-binds it). (def-var! "clojure.core" "decimal?" jolt-decimal?) (def-var! "clojure.core" "==" jolt-num-equiv) ;; chunked-seq? is true for a vector's seq (a real chunked-seq); the overlay's diff --git a/host/chez/predicates.ss b/host/chez/predicates.ss index 8b3734f..9419755 100644 --- a/host/chez/predicates.ss +++ b/host/chez/predicates.ss @@ -12,9 +12,7 @@ (define (jolt-vector? x) (pvec? x)) (define (jolt-set? x) (pset? x)) (define (jolt-seq? x) (or (cseq? x) (empty-list-t? x))) -;; (list? x): a list-marked cseq node or the empty list (). A lazy/vector-backed -;; 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))) +;; list? lives in the overlay (clojure/core/20-coll.clj) — see jolt.host/cseq? etc. (define (jolt-coll-pred? x) (or (pvec? x) (pmap? x) (pset? x) (cseq? x) (empty-list-t? x) (jolt-lazyseq? x))) (define (jolt-number? x) (number? x)) @@ -27,8 +25,9 @@ ;; BigDecimal). decimal? is always false (no BigDecimal type). (define (jolt-integer? x) (and (number? x) (exact? x) (integer? x))) (define (jolt-float? x) (and (number? x) (flonum? x))) -(define (jolt-ratio? x) (and (number? x) (exact? x) (rational? x) (not (integer? x)))) -(define (jolt-rational? x) (and (number? x) (exact? x))) +;; ratio?/rational? live in the overlay (clojure/core/20-coll.clj), built on the +;; jolt.host tower tests. decimal? stays native: the optional bigdec module +;; (java/bigdec.ss) re-binds it to jbigdec?, so it can't be a static overlay const. (define (jolt-decimal? x) #f) (define (jolt-fn? x) (procedure? x)) (define (jolt-boolean-pred? x) (boolean? x)) @@ -61,8 +60,6 @@ (def-var! "clojure.core" "char?" jolt-char-pred?) (def-var! "clojure.core" "integer?" jolt-integer?) (def-var! "clojure.core" "float?" jolt-float?) -(def-var! "clojure.core" "ratio?" jolt-ratio?) -(def-var! "clojure.core" "rational?" jolt-rational?) (def-var! "clojure.core" "decimal?" jolt-decimal?) ;; == numeric value-equality (ignores exactness, unlike =): (== 3 3.0) -> true. ;; 1-arity is trivially true; 2+ args must all be numbers (Numbers.equiv throws @@ -84,7 +81,6 @@ (def-var! "clojure.core" "vector?" jolt-vector?) (def-var! "clojure.core" "set?" jolt-set?) (def-var! "clojure.core" "seq?" jolt-seq?) -(def-var! "clojure.core" "list?" jolt-list-pred?) (def-var! "clojure.core" "coll?" jolt-coll-pred?) (def-var! "clojure.core" "fn?" jolt-fn?) (def-var! "clojure.core" "boolean?" jolt-boolean-pred?) @@ -93,26 +89,22 @@ (def-var! "clojure.core" "namespace" jolt-namespace) ;; --- jolt.host raw type-test primitives ------------------------------------- -;; The clojure.core predicates above bottom out at these host tests. Exposing them -;; under jolt.host lets overlay Clojure build the predicate web (coll?/list?/ratio? -;; …) as pure compositions that lower to exactly these calls — no perf loss. Naming: -;; integer-type?/rational-type? are the Chez TYPE tests (exact integer / exact -;; rational), distinct from clojure.core/integer? (which also gates on number?). -;; map?/set?/seq? are NOT reducible to a single rep test — they are extended at -;; runtime with sorted-collection/record/lazy arms (host-table.ss, records.ss, -;; lazy-bridge.ss) — so only the rep predicates are exposed, not those unions. -;; exact? is wrapped to be TOTAL (Chez's raw exact? errors on a non-number); the -;; rep/flonum/integer/rational tests already return #f for a non-match. +;; Some clojure.core predicates bottom out at host tests overlay Clojure can't +;; reach. Expose the ones the migratable predicates need so the overlay versions +;; lower to exactly these calls — no perf loss. rational-type? is the Chez TYPE +;; test (exact rational), distinct from clojure.core/rational? (which gates on +;; number? first). exact? is wrapped TOTAL (Chez's raw exact? errors on a +;; non-number); rational-type? already returns #f for a non-match. +;; +;; Only the tests consumed by the migrated predicates (ratio?/rational? -> exact?, +;; rational-type?; list? -> cseq?/cseq-list?/empty-list?) are exposed. The rest of +;; the predicate web stays native and is NOT exposed: map?/set?/seq?/coll? are +;; extended at runtime with sorted/record/lazy arms, decimal? is extended by the +;; optional bigdec module, integer?/float? are on the compiler emit/inference path, +;; and vector? is reached by the kernel-tier peek during bootstrap. (define (jh-exact? x) (and (number? x) (exact? x))) (def-var! "jolt.host" "exact?" jh-exact?) -(def-var! "jolt.host" "flonum?" flonum?) -(def-var! "jolt.host" "integer-type?" integer?) (def-var! "jolt.host" "rational-type?" rational?) -(def-var! "jolt.host" "pvec?" pvec?) -(def-var! "jolt.host" "pmap?" pmap?) -(def-var! "jolt.host" "pset?" pset?) (def-var! "jolt.host" "cseq?" cseq?) (def-var! "jolt.host" "empty-list?" empty-list-t?) (def-var! "jolt.host" "cseq-list?" cseq-list?) -;; jolt.host/lazyseq? is exposed in lazy-bridge.ss, where jolt-lazyseq? is defined -;; (it loads after this file, so it can't be referenced here as a value). diff --git a/host/chez/seed/prelude.ss b/host/chez/seed/prelude.ss index 7e0fe87..11549c3 100644 --- a/host/chez/seed/prelude.ss +++ b/host/chez/seed/prelude.ss @@ -205,13 +205,13 @@ (guard (e (#t #f)) (def-var! "clojure.core" "simple-ident?" (letrec ((simple-ident? (lambda (x) (let fnrec4789 ((x x)) (let* ((or__26__auto (jolt-invoke (var-deref "clojure.core" "simple-symbol?") x))) (if (jolt-truthy? or__26__auto) or__26__auto (jolt-invoke (var-deref "clojure.core" "simple-keyword?") x))))))) simple-ident?))) (guard (e (#t #f)) - (def-var! "clojure.core" "ratio?" (letrec ((ratio? (lambda (x) (let fnrec4790 ((x x)) #f)))) ratio?))) + (def-var! "clojure.core" "ratio?" (letrec ((ratio? (lambda (x) (let fnrec4790 ((x x)) (let* ((and__25__auto (jolt-invoke (var-deref "clojure.core" "number?") x))) (if (jolt-truthy? and__25__auto) (let* ((and__25__auto (jolt-invoke (var-deref "jolt.host" "exact?") x))) (if (jolt-truthy? and__25__auto) (let* ((and__25__auto (jolt-invoke (var-deref "jolt.host" "rational-type?") x))) (if (jolt-truthy? and__25__auto) (jolt-not (jolt-invoke (var-deref "clojure.core" "integer?") x)) and__25__auto)) and__25__auto)) and__25__auto)))))) ratio?))) (guard (e (#t #f)) - (def-var! "clojure.core" "decimal?" (letrec ((decimal? (lambda (x) (let fnrec4791 ((x x)) #f)))) decimal?))) + (def-var! "clojure.core" "rational?" (letrec ((rational? (lambda (x) (let fnrec4791 ((x x)) (let* ((and__25__auto (jolt-invoke (var-deref "clojure.core" "number?") x))) (if (jolt-truthy? and__25__auto) (jolt-invoke (var-deref "jolt.host" "exact?") x) and__25__auto)))))) rational?))) (guard (e (#t #f)) (def-var! "clojure.core" "class?" (letrec ((class? (lambda (x) (let fnrec4792 ((x x)) #f)))) class?))) (guard (e (#t #f)) - (def-var! "clojure.core" "rational?" (letrec ((rational? (lambda (x) (let fnrec4793 ((x x)) (jolt-invoke (var-deref "clojure.core" "int?") x))))) rational?))) + (def-var! "clojure.core" "list?" (letrec ((list? (lambda (x) (let fnrec4793 ((x x)) (let* ((or__26__auto (let* ((and__25__auto (jolt-invoke (var-deref "jolt.host" "cseq?") x))) (if (jolt-truthy? and__25__auto) (jolt-invoke (var-deref "jolt.host" "cseq-list?") x) and__25__auto)))) (if (jolt-truthy? or__26__auto) or__26__auto (jolt-invoke (var-deref "jolt.host" "empty-list?") x))))))) list?))) (guard (e (#t #f)) (def-var! "clojure.core" "nat-int?" (letrec ((nat-int? (lambda (x) (let fnrec4794 ((x x)) (let* ((and__25__auto (jolt-invoke (var-deref "clojure.core" "int?") x))) (if (jolt-truthy? and__25__auto) (>= x 0) and__25__auto)))))) nat-int?))) (guard (e (#t #f)) diff --git a/jolt-core/clojure/core/20-coll.clj b/jolt-core/clojure/core/20-coll.clj index e51c6d8..b7c346c 100644 --- a/jolt-core/clojure/core/20-coll.clj +++ b/jolt-core/clojure/core/20-coll.clj @@ -177,13 +177,21 @@ (defn simple-ident? [x] (or (simple-symbol? x) (simple-keyword? x))) -;; Jolt has no ratio or bigdecimal types, so these are constants / reduce to int?. -(defn ratio? [x] false) -(defn decimal? [x] false) -;; No first-class Class objects either: class names are symbols the evaluator -;; handles in instance?/new positions, never values — so nothing is a class. +;; Numeric-tower predicates over the Chez tower (jolt has exact ints, ratios, and +;; flonums). ratio? = exact non-integer; rational? = exact (int or ratio). Built on +;; the jolt.host tower tests so they lower to the same code the native shims did. +;; decimal?/integer?/float?/int?/double? stay native (bigdec-extended or on the +;; compiler emit/inference path) — see predicates.ss. +(defn ratio? [x] + (and (number? x) (jolt.host/exact? x) (jolt.host/rational-type? x) (not (integer? x)))) +(defn rational? [x] (and (number? x) (jolt.host/exact? x))) +;; No first-class Class objects: class names are symbols the evaluator handles in +;; instance?/new positions, never values — so nothing is a class. (defn class? [x] false) -(defn rational? [x] (int? x)) +;; list?: a list-marked cseq node or the empty list (). A lazy/vector-backed seq, +;; (rest list), (seq coll), (map …) are seqs but not lists. Not extended like +;; map?/set?/seq?, so it migrates cleanly. +(defn list? [x] (or (and (jolt.host/cseq? x) (jolt.host/cseq-list? x)) (jolt.host/empty-list? x))) (defn nat-int? [x] (and (int? x) (>= x 0))) (defn neg-int? [x] (and (int? x) (neg? x))) (defn pos-int? [x] (and (int? x) (pos? x)))