From f410bec2ec6017fe44423f6a56b501a3ae6053c9 Mon Sep 17 00:00:00 2001 From: Yogthos Date: Sun, 7 Jun 2026 20:24:24 -0400 Subject: [PATCH] =?UTF-8?q?core:=20Phase=204=20=E2=80=94=20move=20tagged-v?= =?UTF-8?q?alue=20predicates=20to=20the=20overlay?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- jolt-core/clojure/core/20-coll.clj | 12 ++++++++++++ src/jolt/core.janet | 21 ++++++--------------- test/spec/predicates-spec.janet | 15 +++++++++++++++ 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/jolt-core/clojure/core/20-coll.clj b/jolt-core/clojure/core/20-coll.clj index df358bc..a67953f 100644 --- a/jolt-core/clojure/core/20-coll.clj +++ b/jolt-core/clojure/core/20-coll.clj @@ -253,3 +253,15 @@ :else nil))) (defn ex-cause [e] (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) diff --git a/src/jolt/core.janet b/src/jolt/core.janet index 3812ec8..dba2069 100644 --- a/src/jolt/core.janet +++ b/src/jolt/core.janet @@ -1393,8 +1393,7 @@ (defn core-uri? [x] false) (defn core-uuid? [x] false) (defn core-bytes? [x] (buffer? x)) -(defn core-tagged-literal? [x] - (and (table? x) (= :jolt/tagged-literal (get x :jolt/type)))) +# tagged-literal? now lives in the Clojure collection tier (tagged-value predicate). (defn core-meta [x] "Returns the metadata of x, or nil." @@ -1764,7 +1763,7 @@ (defn core-chunk-buffer [capacity] @[]) (defn core-chunk-append [b x] (array/push b x) 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-rest [s] (core-rest s)) (defn core-chunk-next [s] (core-next s)) @@ -1783,8 +1782,7 @@ (core-filter (fn [_] (< (math/random) prob)) (in rest 0)))) (defn core-reader-conditional [form splicing?] @{:jolt/type :jolt/reader-conditional :form form :splicing? splicing?}) -(defn core-reader-conditional? [x] - (and (table? x) (= :jolt/reader-conditional (get x :jolt/type)))) +# reader-conditional? now lives in the Clojure collection tier (tagged-value predicate). (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-array-seq [arr & _] (core-seq arr)) @@ -1869,8 +1867,7 @@ (+= i 2)) atm) -(defn core-atom? [x] - (and (table? x) (= :jolt/atom (x :jolt/type)))) +# atom? now lives in the Clojure collection tier (tagged-value predicate). # 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 @@ -2126,7 +2123,7 @@ # Volatiles — typed box so deref/volatile? can recognize them. (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] (def new-val (apply f (vol :val) args)) (put vol :val new-val) @@ -2537,7 +2534,7 @@ (defn core-special-symbol? [x] (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). (defn core-promise [] (core-atom nil)) @@ -2794,7 +2791,6 @@ "denominator" core-denominator "list*" core-list* "special-symbol?" core-special-symbol? - "record?" core-record? "promise" core-promise "deliver" core-deliver "future-call" core-future-call @@ -2862,7 +2858,6 @@ "ex-info" core-ex-info "prn-str" core-prn-str "println-str" core-println-str - "volatile?" core-volatile? "force" core-force "realized?" core-realized? "delay?" core-delay? @@ -2970,7 +2965,6 @@ "chunk-buffer" core-chunk-buffer "chunk-append" core-chunk-append "chunk" core-chunk - "chunked-seq?" core-chunked-seq? "chunk-first" core-chunk-first "chunk-rest" core-chunk-rest "chunk-next" core-chunk-next @@ -2980,7 +2974,6 @@ "disj!" core-disj! "random-sample" core-random-sample "reader-conditional" core-reader-conditional - "reader-conditional?" core-reader-conditional? "sorted-map-by" core-sorted-map-by "sorted-set-by" core-sorted-set-by "array-seq" core-array-seq @@ -3042,7 +3035,6 @@ # Hash "hash" core-hash "atom" core-atom - "atom?" core-atom? "deref" core-deref "reset!" core-reset! "swap!" core-swap! @@ -3097,7 +3089,6 @@ "uri?" core-uri? "uuid?" core-uuid? "bytes?" core-bytes? - "tagged-literal?" core-tagged-literal? "meta" core-meta "var-get" core-var-get "var-set" core-var-set diff --git a/test/spec/predicates-spec.janet b/test/spec/predicates-spec.janet index 601d148..fd8ea0a 100644 --- a/test/spec/predicates-spec.janet +++ b/test/spec/predicates-spec.janet @@ -88,6 +88,21 @@ ["keyword-identical?" "true" "(keyword-identical? :a :a)"] ["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" ["= same" "true" "(= 1 1)"] ["= vectors" "true" "(= [1 2] [1 2])"]