Implement deref-as-shift: ReactiveResetFrame, DerefFrame, continuation capture
frames.sx: ReactiveResetFrame + DerefFrame constructors, kont-capture-to-reactive-reset, has-reactive-reset-frame?. cek.sx: deref as CEK special form, step-sf-deref pushes DerefFrame, reactive-shift-deref captures continuation as signal subscriber, ReactiveResetFrame in step-continue calls update-fn on re-render. adapter-dom.sx: cek-reactive-text/cek-reactive-attr using cek-run with ReactiveResetFrame for implicit DOM bindings. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -156,6 +156,9 @@
|
||||
(= name "reset") (step-sf-reset args env kont)
|
||||
(= name "shift") (step-sf-shift args env kont)
|
||||
|
||||
;; Reactive deref-as-shift
|
||||
(= name "deref") (step-sf-deref args env kont)
|
||||
|
||||
;; Scoped effects
|
||||
(= name "scope") (step-sf-scope args env kont)
|
||||
(= name "provide") (step-sf-provide args env kont)
|
||||
@@ -397,6 +400,55 @@
|
||||
(make-cek-state body shift-env rest-kont))))))
|
||||
|
||||
|
||||
;; deref: evaluate argument, push DerefFrame
|
||||
(define step-sf-deref
|
||||
(fn (args env kont)
|
||||
(make-cek-state
|
||||
(first args) env
|
||||
(kont-push (make-deref-frame env) kont))))
|
||||
|
||||
;; reactive-shift-deref: the heart of deref-as-shift
|
||||
;; When deref encounters a signal inside a reactive-reset boundary,
|
||||
;; capture the continuation up to the reactive-reset as the subscriber.
|
||||
(define reactive-shift-deref
|
||||
(fn (sig env kont)
|
||||
(let ((scan-result (kont-capture-to-reactive-reset kont))
|
||||
(captured-frames (first scan-result))
|
||||
(reset-frame (nth scan-result 1))
|
||||
(remaining-kont (nth scan-result 2))
|
||||
(update-fn (get reset-frame "update-fn")))
|
||||
;; Sub-scope for nested subscriber cleanup on re-invocation
|
||||
(let ((sub-disposers (list)))
|
||||
(let ((subscriber
|
||||
(fn ()
|
||||
;; Dispose previous nested subscribers
|
||||
(for-each (fn (d) (invoke d)) sub-disposers)
|
||||
(set! sub-disposers (list))
|
||||
;; Re-invoke: push fresh ReactiveResetFrame (first-render=false)
|
||||
(let ((new-reset (make-reactive-reset-frame env update-fn false))
|
||||
(new-kont (concat captured-frames
|
||||
(list new-reset)
|
||||
remaining-kont)))
|
||||
(with-island-scope
|
||||
(fn (d) (append! sub-disposers d))
|
||||
(fn ()
|
||||
(cek-run
|
||||
(make-cek-value (signal-value sig) env new-kont))))))))
|
||||
;; Register subscriber
|
||||
(signal-add-sub! sig subscriber)
|
||||
;; Register cleanup with island scope
|
||||
(register-in-scope
|
||||
(fn ()
|
||||
(signal-remove-sub! sig subscriber)
|
||||
(for-each (fn (d) (invoke d)) sub-disposers)))
|
||||
;; Initial render: value flows through captured frames + reset (first-render=true)
|
||||
;; so the full expression completes normally
|
||||
(let ((initial-kont (concat captured-frames
|
||||
(list reset-frame)
|
||||
remaining-kont)))
|
||||
(make-cek-value (signal-value sig) env initial-kont)))))))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; 6. Function call step handler
|
||||
;; --------------------------------------------------------------------------
|
||||
@@ -702,6 +754,37 @@
|
||||
(= ft "reset")
|
||||
(make-cek-value value env rest-k)
|
||||
|
||||
;; --- DerefFrame: deref argument evaluated ---
|
||||
(= ft "deref")
|
||||
(let ((val value)
|
||||
(fenv (get frame "env")))
|
||||
(if (not (signal? val))
|
||||
;; Not a signal: pass through
|
||||
(make-cek-value val fenv rest-k)
|
||||
;; Signal: check for ReactiveResetFrame
|
||||
(if (has-reactive-reset-frame? rest-k)
|
||||
;; Perform reactive shift
|
||||
(reactive-shift-deref val fenv rest-k)
|
||||
;; No reactive-reset: normal deref (scope-based tracking)
|
||||
(do
|
||||
(let ((ctx (context "sx-reactive" nil)))
|
||||
(when ctx
|
||||
(let ((dep-list (get ctx "deps"))
|
||||
(notify-fn (get ctx "notify")))
|
||||
(when (not (contains? dep-list val))
|
||||
(append! dep-list val)
|
||||
(signal-add-sub! val notify-fn)))))
|
||||
(make-cek-value (signal-value val) fenv rest-k)))))
|
||||
|
||||
;; --- ReactiveResetFrame: expression completed ---
|
||||
(= ft "reactive-reset")
|
||||
(let ((update-fn (get frame "update-fn"))
|
||||
(first? (get frame "first-render")))
|
||||
;; On re-render (not first), call update-fn with new value
|
||||
(when (and update-fn (not first?))
|
||||
(invoke update-fn value))
|
||||
(make-cek-value value env rest-k))
|
||||
|
||||
;; --- ScopeFrame: body result ---
|
||||
(= ft "scope")
|
||||
(let ((name (get frame "name"))
|
||||
|
||||
Reference in New Issue
Block a user