core: Phase 4 — move tagged-value predicates to the overlay

atom?/volatile?/reader-conditional?/tagged-literal?/record?/chunked-seq? all
just read a value's kind from :jolt/type (records from :jolt/deftype), and get
returns nil on non-tables, so the predicates are pure over get with no host
surface. Constructors stay native. delay?/transient? keep their Janet defns —
they have internal callers (force, conj/count/persistent! etc.). chunked-seq?
is always false (no chunked seqs until Phase 5). Added a tagged-value spec
group covering positive and negative cases for each.
This commit is contained in:
Yogthos 2026-06-07 20:24:24 -04:00
parent 7f68a5872c
commit f410bec2ec
3 changed files with 33 additions and 15 deletions

View file

@ -253,3 +253,15 @@
:else nil))) :else nil)))
(defn ex-cause [e] (defn ex-cause [e]
(let [e (ex-unwrap e)] (if (ex-info-val? e) (get e :cause) nil))) (let [e (ex-unwrap e)] (if (ex-info-val? e) (get e :cause) nil)))
;; Tagged-value predicates. The constructors (atom/volatile!/...) stay in Janet,
;; but every tagged value carries its kind under :jolt/type (records under
;; :jolt/deftype), reachable via get — which is nil on non-tables — so the
;; predicates are pure over get and move out of the seed.
(defn atom? [x] (= (get x :jolt/type) :jolt/atom))
(defn volatile? [x] (= (get x :jolt/type) :jolt/volatile))
(defn reader-conditional? [x] (= (get x :jolt/type) :jolt/reader-conditional))
(defn tagged-literal? [x] (= (get x :jolt/type) :jolt/tagged-literal))
(defn record? [x] (some? (get x :jolt/deftype)))
;; Jolt has no chunked seqs (Phase 5 territory), so this is always false.
(defn chunked-seq? [x] false)

View file

@ -1393,8 +1393,7 @@
(defn core-uri? [x] false) (defn core-uri? [x] false)
(defn core-uuid? [x] false) (defn core-uuid? [x] false)
(defn core-bytes? [x] (buffer? x)) (defn core-bytes? [x] (buffer? x))
(defn core-tagged-literal? [x] # tagged-literal? now lives in the Clojure collection tier (tagged-value predicate).
(and (table? x) (= :jolt/tagged-literal (get x :jolt/type))))
(defn core-meta [x] (defn core-meta [x]
"Returns the metadata of x, or nil." "Returns the metadata of x, or nil."
@ -1764,7 +1763,7 @@
(defn core-chunk-buffer [capacity] @[]) (defn core-chunk-buffer [capacity] @[])
(defn core-chunk-append [b x] (array/push b x) b) (defn core-chunk-append [b x] (array/push b x) b)
(defn core-chunk [b] b) (defn core-chunk [b] b)
(defn core-chunked-seq? [x] false) # chunked-seq? now lives in the Clojure collection tier (always false on Jolt).
(defn core-chunk-first [s] (core-first s)) (defn core-chunk-first [s] (core-first s))
(defn core-chunk-rest [s] (core-rest s)) (defn core-chunk-rest [s] (core-rest s))
(defn core-chunk-next [s] (core-next s)) (defn core-chunk-next [s] (core-next s))
@ -1783,8 +1782,7 @@
(core-filter (fn [_] (< (math/random) prob)) (in rest 0)))) (core-filter (fn [_] (< (math/random) prob)) (in rest 0))))
(defn core-reader-conditional [form splicing?] (defn core-reader-conditional [form splicing?]
@{:jolt/type :jolt/reader-conditional :form form :splicing? splicing?}) @{:jolt/type :jolt/reader-conditional :form form :splicing? splicing?})
(defn core-reader-conditional? [x] # reader-conditional? now lives in the Clojure collection tier (tagged-value predicate).
(and (table? x) (= :jolt/reader-conditional (get x :jolt/type))))
(defn core-sorted-map-by [cmp & kvs] (apply core-sorted-map kvs)) (defn core-sorted-map-by [cmp & kvs] (apply core-sorted-map kvs))
(defn core-sorted-set-by [cmp & xs] (apply core-sorted-set xs)) (defn core-sorted-set-by [cmp & xs] (apply core-sorted-set xs))
(defn core-array-seq [arr & _] (core-seq arr)) (defn core-array-seq [arr & _] (core-seq arr))
@ -1869,8 +1867,7 @@
(+= i 2)) (+= i 2))
atm) atm)
(defn core-atom? [x] # atom? now lives in the Clojure collection tier (tagged-value predicate).
(and (table? x) (= :jolt/atom (x :jolt/type))))
# Futures — run the body on a real OS thread (ev/thread) for true parallelism. # Futures — run the body on a real OS thread (ev/thread) for true parallelism.
# Janet threads have separate heaps, so the thunk and the state it closes over are # Janet threads have separate heaps, so the thunk and the state it closes over are
@ -2126,7 +2123,7 @@
# Volatiles — typed box so deref/volatile? can recognize them. # Volatiles — typed box so deref/volatile? can recognize them.
(defn core-volatile! [v] @{:jolt/type :jolt/volatile :val v}) (defn core-volatile! [v] @{:jolt/type :jolt/volatile :val v})
(defn core-volatile? [x] (and (table? x) (= :jolt/volatile (x :jolt/type)))) # volatile? now lives in the Clojure collection tier (tagged-value predicate).
(defn core-vswap! [vol f & args] (defn core-vswap! [vol f & args]
(def new-val (apply f (vol :val) args)) (def new-val (apply f (vol :val) args))
(put vol :val new-val) (put vol :val new-val)
@ -2537,7 +2534,7 @@
(defn core-special-symbol? [x] (defn core-special-symbol? [x]
(and (core-symbol? x) (= true (get special-syms (x :name))))) (and (core-symbol? x) (= true (get special-syms (x :name)))))
(defn core-record? [x] (and (table? x) (not (nil? (get x :jolt/deftype))))) # record? now lives in the Clojure collection tier (tagged-value predicate).
# Promise: single-threaded box backed by an atom (deref returns nil until set). # Promise: single-threaded box backed by an atom (deref returns nil until set).
(defn core-promise [] (core-atom nil)) (defn core-promise [] (core-atom nil))
@ -2794,7 +2791,6 @@
"denominator" core-denominator "denominator" core-denominator
"list*" core-list* "list*" core-list*
"special-symbol?" core-special-symbol? "special-symbol?" core-special-symbol?
"record?" core-record?
"promise" core-promise "promise" core-promise
"deliver" core-deliver "deliver" core-deliver
"future-call" core-future-call "future-call" core-future-call
@ -2862,7 +2858,6 @@
"ex-info" core-ex-info "ex-info" core-ex-info
"prn-str" core-prn-str "prn-str" core-prn-str
"println-str" core-println-str "println-str" core-println-str
"volatile?" core-volatile?
"force" core-force "force" core-force
"realized?" core-realized? "realized?" core-realized?
"delay?" core-delay? "delay?" core-delay?
@ -2970,7 +2965,6 @@
"chunk-buffer" core-chunk-buffer "chunk-buffer" core-chunk-buffer
"chunk-append" core-chunk-append "chunk-append" core-chunk-append
"chunk" core-chunk "chunk" core-chunk
"chunked-seq?" core-chunked-seq?
"chunk-first" core-chunk-first "chunk-first" core-chunk-first
"chunk-rest" core-chunk-rest "chunk-rest" core-chunk-rest
"chunk-next" core-chunk-next "chunk-next" core-chunk-next
@ -2980,7 +2974,6 @@
"disj!" core-disj! "disj!" core-disj!
"random-sample" core-random-sample "random-sample" core-random-sample
"reader-conditional" core-reader-conditional "reader-conditional" core-reader-conditional
"reader-conditional?" core-reader-conditional?
"sorted-map-by" core-sorted-map-by "sorted-map-by" core-sorted-map-by
"sorted-set-by" core-sorted-set-by "sorted-set-by" core-sorted-set-by
"array-seq" core-array-seq "array-seq" core-array-seq
@ -3042,7 +3035,6 @@
# Hash # Hash
"hash" core-hash "hash" core-hash
"atom" core-atom "atom" core-atom
"atom?" core-atom?
"deref" core-deref "deref" core-deref
"reset!" core-reset! "reset!" core-reset!
"swap!" core-swap! "swap!" core-swap!
@ -3097,7 +3089,6 @@
"uri?" core-uri? "uri?" core-uri?
"uuid?" core-uuid? "uuid?" core-uuid?
"bytes?" core-bytes? "bytes?" core-bytes?
"tagged-literal?" core-tagged-literal?
"meta" core-meta "meta" core-meta
"var-get" core-var-get "var-get" core-var-get
"var-set" core-var-set "var-set" core-var-set

View file

@ -88,6 +88,21 @@
["keyword-identical?" "true" "(keyword-identical? :a :a)"] ["keyword-identical?" "true" "(keyword-identical? :a :a)"]
["keyword-identical? no" "false" "(keyword-identical? :a :b)"]) ["keyword-identical? no" "false" "(keyword-identical? :a :b)"])
# Tagged-value predicates moved to the overlay in Phase 4 (read the value's
# :jolt/type via get). The constructors stay native.
(defspec "predicates / tagged-value (Phase 4)"
["atom? yes" "true" "(atom? (atom 1))"]
["atom? no" "false" "(atom? 1)"]
["volatile? yes" "true" "(volatile? (volatile! 1))"]
["volatile? no" "false" "(volatile? (atom 1))"]
["record? yes" "true" "(do (defrecord Rp [a]) (record? (->Rp 1)))"]
["record? no map" "false" "(record? {:a 1})"]
["record? no nil" "false" "(record? nil)"]
["tagged-literal? yes" "true" "(tagged-literal? (tagged-literal (quote inst) \"2020\"))"]
["tagged-literal? no" "false" "(tagged-literal? 1)"]
["reader-conditional? no" "false" "(reader-conditional? 1)"]
["chunked-seq? always false" "false" "(chunked-seq? (seq [1 2 3]))"])
(defspec "predicates / equality & identity" (defspec "predicates / equality & identity"
["= same" "true" "(= 1 1)"] ["= same" "true" "(= 1 1)"]
["= vectors" "true" "(= [1 2] [1 2])"] ["= vectors" "true" "(= [1 2] [1 2])"]