Chez Phase 2 (inc S): atom watches + validators (jolt-mn9o)
The Chez atom record gains watches (an alist of key->fn) and validator slots. swap!/reset! now validate the candidate value before storing and notify watches after, in the seed's order (core_refs.janet) — the watch fn is called (key ref old new). compare-and-set!/swap-vals!/reset-vals! route through reset!/swap! so they validate + notify too. add-watch/remove-watch/set-validator!/get-validator are native here and re-asserted in post-prelude.ss: the clojure.core overlay implements them via jolt.host/ref-put! on (get atom :watches), a Janet-table mutation a Chez atom record can't answer, so its def-var! would otherwise clobber these. set- validator! validates the current value immediately (Clojure throws if already invalid). Parity 2150 -> 2154, 0 new divergences. New test/chez/_atomwatch.janet 10/10. The (class x) native and the clojure.walk port were explored this increment but deferred: class alone makes the String-class-token corpus cases emit-and- run with a wrong value (the bare token doesn't resolve yet) — filed jolt-13zk to land the native together with token resolution; clojure.walk is blocked on list? + map-entry-as-vector (jolt-75sv).
This commit is contained in:
parent
1f96351acb
commit
b7864100cb
4 changed files with 135 additions and 12 deletions
|
|
@ -11,11 +11,36 @@
|
|||
;; self-sufficient for atoms without the full prelude (the overlay versions, when
|
||||
;; the full prelude loads, override these but compose the same native kernel).
|
||||
|
||||
(define-record-type jolt-atom (fields (mutable val)) (nongenerative jolt-atom-v1))
|
||||
;; watches is an alist of (key . watch-fn); validator is a jolt fn or jolt-nil.
|
||||
;; The overlay's add-watch/set-validator! drive these via jolt.host/ref-put! on a
|
||||
;; Janet table, which a Chez atom record is not — so the peripheral ops + the
|
||||
;; notify/validate behaviour live natively here, and post-prelude.ss re-asserts
|
||||
;; them over the overlay's def-var! (jolt-mn9o).
|
||||
(define-record-type jolt-atom
|
||||
(fields (mutable val) (mutable watches) (mutable validator))
|
||||
(nongenerative jolt-atom-v2))
|
||||
|
||||
;; (atom init) — extra :meta/:validator opts are accepted and ignored for now
|
||||
;; (watches/validators are overlay features layered via jolt.host/ref-put!).
|
||||
(define (jolt-atom-new v . _opts) (make-jolt-atom v))
|
||||
;; (atom init) / (atom init :validator f :meta m): scan the trailing keyword opts
|
||||
;; for :validator (the only one with runtime behaviour; :meta is accepted/ignored).
|
||||
(define (jolt-atom-new v . opts)
|
||||
(let loop ((o opts) (validator jolt-nil))
|
||||
(cond
|
||||
((or (null? o) (null? (cdr o))) (make-jolt-atom v '() validator))
|
||||
((and (keyword-t? (car o)) (string=? (keyword-t-name (car o)) "validator"))
|
||||
(loop (cddr o) (cadr o)))
|
||||
(else (loop (cddr o) validator)))))
|
||||
|
||||
;; validate a candidate value: a non-nil validator that returns falsey rejects.
|
||||
(define (jolt-atom-validate a v)
|
||||
(let ((vf (jolt-atom-validator a)))
|
||||
(when (and (not (jolt-nil? vf)) (jolt-not (jolt-invoke vf v)))
|
||||
(error #f "Invalid reference state"))))
|
||||
|
||||
;; notify each watch (k ref old new), in insertion order (alist is reverse-built,
|
||||
;; so walk it reversed to match add order — matches the seed's :pairs iteration).
|
||||
(define (jolt-atom-notify a old new)
|
||||
(for-each (lambda (kv) (jolt-invoke (cdr kv) (car kv) a old new))
|
||||
(reverse (jolt-atom-watches a))))
|
||||
|
||||
;; deref reads an atom; it also unwraps a `reduced` (Clojure @(reduced x) => x,
|
||||
;; which the overlay's `unreduced` relies on). The reduced record is in seq.ss.
|
||||
|
|
@ -25,31 +50,61 @@
|
|||
((jolt-reduced? x) (jolt-reduced-val x))
|
||||
(else (error #f "deref: unsupported reference type" x))))
|
||||
|
||||
;; (swap! a f arg*) -> (reset! a (f @a arg*)); f is invoked through jolt-invoke
|
||||
;; (a jolt fn value, keyword, or invokable collection).
|
||||
;; (swap! a f arg*) -> (reset! a (f @a arg*)); f is invoked through jolt-invoke.
|
||||
;; Validate the new value BEFORE storing, notify watches AFTER (the seed order).
|
||||
(define (jolt-swap! a f . args)
|
||||
(let ((nv (apply jolt-invoke f (jolt-atom-val a) args)))
|
||||
(let* ((old (jolt-atom-val a))
|
||||
(nv (apply jolt-invoke f old args)))
|
||||
(jolt-atom-validate a nv)
|
||||
(jolt-atom-val-set! a nv)
|
||||
(jolt-atom-notify a old nv)
|
||||
nv))
|
||||
|
||||
(define (jolt-reset! a v) (jolt-atom-val-set! a v) v)
|
||||
(define (jolt-reset! a v)
|
||||
(let ((old (jolt-atom-val a)))
|
||||
(jolt-atom-validate a v)
|
||||
(jolt-atom-val-set! a v)
|
||||
(jolt-atom-notify a old v)
|
||||
v))
|
||||
|
||||
(define (jolt-compare-and-set! a oldv newv)
|
||||
(if (jolt= (jolt-atom-val a) oldv)
|
||||
(begin (jolt-atom-val-set! a newv) #t)
|
||||
(begin (jolt-reset! a newv) #t)
|
||||
#f))
|
||||
|
||||
(define (jolt-swap-vals! a f . args)
|
||||
(let* ((old (jolt-atom-val a))
|
||||
(nv (apply jolt-invoke f old args)))
|
||||
(jolt-atom-val-set! a nv)
|
||||
(jolt-reset! a nv)
|
||||
(jolt-vector old nv)))
|
||||
|
||||
(define (jolt-reset-vals! a v)
|
||||
(let ((old (jolt-atom-val a)))
|
||||
(jolt-atom-val-set! a v)
|
||||
(jolt-reset! a v)
|
||||
(jolt-vector old v)))
|
||||
|
||||
;; --- watches / validators (jolt-mn9o) ---------------------------------------
|
||||
;; add-watch interns (key . fn) (replacing any existing key, keeping order);
|
||||
;; remove-watch drops it; both return the atom. set-validator! installs a
|
||||
;; validator and validates the CURRENT value immediately (Clojure throws if it's
|
||||
;; already invalid); get-validator reads the slot.
|
||||
(define (jolt-add-watch a key f)
|
||||
(jolt-atom-watches-set! a
|
||||
(cons (cons key f)
|
||||
(remp (lambda (kv) (jolt=2 (car kv) key)) (jolt-atom-watches a))))
|
||||
a)
|
||||
(define (jolt-remove-watch a key)
|
||||
(jolt-atom-watches-set! a
|
||||
(remp (lambda (kv) (jolt=2 (car kv) key)) (jolt-atom-watches a)))
|
||||
a)
|
||||
(define (jolt-set-validator! a f)
|
||||
(let ((vf (if (jolt-nil? f) jolt-nil f)))
|
||||
(when (and (not (jolt-nil? vf)) (jolt-not (jolt-invoke vf (jolt-atom-val a))))
|
||||
(error #f "Invalid reference state"))
|
||||
(jolt-atom-validator-set! a vf)
|
||||
jolt-nil))
|
||||
(define (jolt-get-validator a) (jolt-atom-validator a))
|
||||
|
||||
(def-var! "clojure.core" "atom" jolt-atom-new)
|
||||
(def-var! "clojure.core" "deref" jolt-deref)
|
||||
(def-var! "clojure.core" "swap!" jolt-swap!)
|
||||
|
|
@ -58,3 +113,9 @@
|
|||
(def-var! "clojure.core" "swap-vals!" jolt-swap-vals!)
|
||||
(def-var! "clojure.core" "reset-vals!" jolt-reset-vals!)
|
||||
(def-var! "clojure.core" "atom?" jolt-atom?)
|
||||
;; peripheral ops: the overlay (20-coll) re-defs these over jolt.host/ref-put!,
|
||||
;; which fails on a Chez atom record — post-prelude.ss re-asserts the natives.
|
||||
(def-var! "clojure.core" "add-watch" jolt-add-watch)
|
||||
(def-var! "clojure.core" "remove-watch" jolt-remove-watch)
|
||||
(def-var! "clojure.core" "set-validator!" jolt-set-validator!)
|
||||
(def-var! "clojure.core" "get-validator" jolt-get-validator)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue