From 4d5aa8c903639325ae3cd4ce30b67ed0eb962e4e Mon Sep 17 00:00:00 2001 From: Yogthos Date: Sun, 7 Jun 2026 21:22:35 -0400 Subject: [PATCH] =?UTF-8?q?core:=20Phase=204=20=E2=80=94=20move=20atom=20p?= =?UTF-8?q?eripheral=20ops=20to=20the=20overlay=20over=20a=20host=20primit?= =?UTF-8?q?ive?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First host-primitive batch. Adds jolt.host/ref-put! — the minimal mutation kernel (set/remove a key on a mutable reference cell) the overlay can't express over core fns — and moves seven atom ops out of the seed: swap-vals!/reset-vals!/compare-and-set! compose the native swap!/reset!/deref (which already validate and notify watches); get-validator is a slot read; add-watch/remove-watch/set-validator! mutate the atom (or its watches table) via ref-put!. atom/swap!/reset!/deref and atom-validate/atom-notify-watches stay native — the compiler depends on them and they're hot. compare-and-set! keeps value-equality semantics, matching the prior Janet behavior. Covered by the existing state spec. --- jolt-core/clojure/core/20-coll.clj | 21 ++++++++++++ src/jolt/core.janet | 51 +++--------------------------- src/jolt/host_iface.janet | 7 ++++ 3 files changed, 32 insertions(+), 47 deletions(-) diff --git a/jolt-core/clojure/core/20-coll.clj b/jolt-core/clojure/core/20-coll.clj index a67953f..1da5612 100644 --- a/jolt-core/clojure/core/20-coll.clj +++ b/jolt-core/clojure/core/20-coll.clj @@ -265,3 +265,24 @@ (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) + +;; Atom peripheral operations. atom/swap!/reset!/deref stay native — the compiler +;; depends on them and they're hot. swap-vals!/reset-vals!/compare-and-set! compose +;; the native ops (which already validate and notify watches); get-validator reads a +;; slot; add-watch/remove-watch/set-validator! mutate the atom (or its watches +;; sub-table) through the one host primitive jolt.host/ref-put! — the minimal +;; mutation kernel the overlay can't express over core fns (a nil value removes the +;; key). compare-and-set! compares by value, matching the prior Janet behavior. +(defn swap-vals! [a f & args] + (let [old (deref a)] [old (apply swap! a f args)])) +(defn reset-vals! [a newval] + (let [old (deref a)] (reset! a newval) [old newval])) +(defn compare-and-set! [a oldval newval] + (if (= oldval (deref a)) (do (reset! a newval) true) false)) +(defn get-validator [a] (get a :validator)) +(defn add-watch [a key f] + (jolt.host/ref-put! (get a :watches) key f) a) +(defn remove-watch [a key] + (jolt.host/ref-put! (get a :watches) key nil) a) +(defn set-validator! [a f] + (jolt.host/ref-put! a :validator f) nil) diff --git a/src/jolt/core.janet b/src/jolt/core.janet index dba2069..fa6b8c2 100644 --- a/src/jolt/core.janet +++ b/src/jolt/core.janet @@ -1975,46 +1975,10 @@ (atom-notify-watches atm old-val new-val) new-val) -(defn core-reset-vals! [atm val] - (let [old-val (atm :value)] - (atom-validate atm val) - (put atm :value val) - (atom-notify-watches atm old-val val) - [old-val val])) - -(defn core-swap-vals! [atm f & args] - (var old-val (atm :value)) - (var new-val (apply f old-val args)) - (atom-validate atm new-val) - (put atm :value new-val) - (atom-notify-watches atm old-val new-val) - [old-val new-val]) - -(defn core-compare-and-set! [atm old-val new-val] - (if (= old-val (atm :value)) - (do - (atom-validate atm new-val) - (put atm :value new-val) - (atom-notify-watches atm old-val new-val) - true) - false)) - -(defn core-set-validator! [atm validator-fn] - (put atm :validator validator-fn) - nil) - -(defn core-get-validator [atm] - (atm :validator)) - -(defn core-add-watch [atm key watch-fn] - (let [watches (atm :watches)] - (put watches key watch-fn) - atm)) - -(defn core-remove-watch [atm key] - (let [watches (atm :watches)] - (put watches key nil) - atm)) +# Atom peripheral ops (swap-vals!/reset-vals!/compare-and-set!/get-validator/ +# add-watch/remove-watch/set-validator!) now live in the Clojure collection tier — +# composed over the native atom ops + jolt.host/ref-put!. atom/swap!/reset!/deref +# and the atom-validate/atom-notify-watches helpers stay native (compiler-critical). # ============================================================ # Threading macros (as regular functions? No, as macros in Clojure) @@ -3038,13 +3002,6 @@ "deref" core-deref "reset!" core-reset! "swap!" core-swap! - "swap-vals!" core-swap-vals! - "reset-vals!" core-reset-vals! - "compare-and-set!" core-compare-and-set! - "set-validator!" core-set-validator! - "get-validator" core-get-validator - "add-watch" core-add-watch - "remove-watch" core-remove-watch "not" core-not "derive" core-derive "isa?" core-isa? diff --git a/src/jolt/host_iface.janet b/src/jolt/host_iface.janet index 0c2733b..ff1b37e 100644 --- a/src/jolt/host_iface.janet +++ b/src/jolt/host_iface.janet @@ -148,8 +148,15 @@ (defn h-syntax-quote-lower [ctx inner] (syntax-quote-lower ctx inner)) +# Runtime host primitive: set a key on a mutable reference cell (an atom, the +# watches sub-table, ...). The minimal mutation kernel the overlay can't express +# over core fns — putting nil removes the key (Janet table semantics). Returns the +# table so callers can thread; overlay wrappers return the Clojure-meaningful value. +(defn h-ref-put! [tab key val] (put tab key val) tab) + (def- exports {"form-sym?" h-sym? "form-sym-name" h-sym-name "form-sym-ns" h-sym-ns + "ref-put!" h-ref-put! "form-sym-meta" h-sym-meta "form-list?" h-list? "form-vec?" h-vector? "form-map?" h-map? "form-set?" h-set? "form-char?" h-char? "form-literal?" h-literal?