core: Phase 4 — move atom peripheral ops to the overlay over a host primitive

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.
This commit is contained in:
Yogthos 2026-06-07 21:22:35 -04:00
parent f410bec2ec
commit 4d5aa8c903
3 changed files with 32 additions and 47 deletions

View file

@ -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)

View file

@ -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?

View file

@ -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?