Initial experiments with 'transients' i probably will abandon

This commit is contained in:
Vivianne 2024-07-29 20:20:19 -04:00
parent 8277922bd9
commit 34148672ec

View file

@ -274,22 +274,13 @@
(define (constraint-propagator name cells build) (define (constraint-propagator name cells build)
(compound-propagator name cells cells build)) (compound-propagator name cells cells build))
(define-record-type <reactive-id> (define-record-type <reactive-id>
(%make-reactive-id name clock) (%make-reactive-id clock)
reactive-id? reactive-id?
(name reactive-id-name)
(clock reactive-id-clock set-reactive-id-clock!)) (clock reactive-id-clock set-reactive-id-clock!))
(define (print-reactive-id id port) (define (make-reactive-id)
(display "#<reactive-id " port) (%make-reactive-id 0))
(display (reactive-id-name id) port)
(display ">" port))
(set-record-type-printer! <reactive-id> print-reactive-id)
(define (make-reactive-id name)
(%make-reactive-id name 0))
(define (reactive-id-tick! id) (define (reactive-id-tick! id)
(let ((t (1+ (reactive-id-clock id)))) (let ((t (1+ (reactive-id-clock id))))
@ -367,10 +358,28 @@
(#f nothing) (#f nothing)
(timestamps (make-ephemeral (apply proc args) timestamps)))))) (timestamps (make-ephemeral (apply proc args) timestamps))))))
(define-record-type <transient>
(%make-transient cell id)
transient?
(cell transient-cell)
(id transient-id))
(define* (make-transient cell #:optional (id (make-reactive-id)))
(%make-transient cell id))
(define (set-trans! transient value)
(match transient
(($ <transient> cell id)
(let ((timestamp (reactive-id-tick! id)))
(add-cell-content! cell (make-ephemeral value timestamp))))))
(define (get-trans transient)
(ephemeral-value (cell-content (transient-cell transient))))
(define* (primitive-reactive-propagator name proc) (define* (primitive-reactive-propagator name proc)
(primitive-propagator name (ephemeral-wrap proc))) (primitive-propagator name (ephemeral-wrap proc)))
(define-syntax-rule (define-primitive-reactive-propagator name proc) (define-syntax-rule (define-primitive name proc)
(define name (primitive-reactive-propagator 'name proc))) (define name (primitive-reactive-propagator 'name proc)))
(define-syntax-rule (define-constraint (name cells ...) body ...) (define-syntax-rule (define-constraint (name cells ...) body ...)
@ -380,16 +389,16 @@
(list cells ...) (list cells ...)
(lambda () body ...)))) (lambda () body ...))))
(define-syntax pbind (define-syntax thru
(syntax-rules (->) (syntax-rules (->)
((pbind (inputs ... -> output) body ...) ((thru (inputs ... -> output) body ...)
(begin (begin
((primitive-reactive-propagator ((primitive-reactive-propagator
'anonymous-propa 'anonymous-propa
(lambda (inputs ...) body ...)) (lambda (inputs ...) body ...))
inputs ... output) inputs ... output)
output)) output))
((pbind (inputs ...) body ...) ((thru (inputs ...) body ...)
(let ((output (make-cell 'anonymous-cell))) (let ((output (make-cell 'anonymous-cell)))
((primitive-reactive-propagator ((primitive-reactive-propagator
'anonymous-propa 'anonymous-propa
@ -408,16 +417,15 @@
(make-propagator 'r:attribute (list input) '() activate))) (make-propagator 'r:attribute (list input) '() activate)))
(define-record-type <binding> (define-record-type <binding>
(make-binding id cell default numeric? group) (make-binding trans default numeric? group)
binding? binding?
(id binding-id) (trans binding-trans)
(cell binding-cell)
(default binding-default) (default binding-default)
(numeric? binding-numeric?) (numeric? binding-numeric?)
(group binding-group)) (group binding-group))
(define* (binding id cell #:key (default nothing) (numeric? #f) (group '())) (define* (binding trans #:key (default nothing) (numeric? #f) (group '()))
(make-binding id cell default numeric? group)) (make-binding trans default numeric? group))
(define (obj->string obj) (define (obj->string obj)
(if (string? obj) (if (string? obj)
@ -431,10 +439,10 @@
(define* (r:binding binding elem) (define* (r:binding binding elem)
(match binding (match binding
(($ <binding> id cell default numeric? group) (($ <binding> ($ <transient> cell id) default numeric? group)
(define (update new) (define (update new)
(unless (nothing? new) (unless (nothing? new)
(let ((timestamp (reactive-id-tick! id))) (let* ((timestamp (reactive-id-tick! id)))
(add-cell-content! cell (make-ephemeral new timestamp)) (add-cell-content! cell (make-ephemeral new timestamp))
;; Freshen timestamps for all cells in the same group. ;; Freshen timestamps for all cells in the same group.
(for-each (lambda (other) (for-each (lambda (other)
@ -504,7 +512,10 @@
;; The value attribute is special and can be ;; The value attribute is special and can be
;; used to setup a 2-way data binding. ;; used to setup a 2-way data binding.
(('value (? binding? binding)) (('value (? binding? binding))
(r:binding binding elem)))) (r:binding binding elem))
(('event (? string? event-name) (? procedure? proc))
(add-event-listener! elem event-name
(procedure->external proc)))))
attrs) attrs)
(add-children children)) (add-children children))
(children (add-children children))) (children (add-children children)))
@ -520,169 +531,47 @@
(_ (values)))) (_ (values))))
(make-propagator 'dom (list input) '() activate)) (make-propagator 'dom (list input) '() activate))
(define-record-type <rgb-color> ;; (define-primitive r:hsv->rgb hsv->rgb)
(rgb-color r g b)
rgb-color?
(r rgb-color-r)
(g rgb-color-g)
(b rgb-color-b))
(define-record-type <hsv-color> ;;(define-constraint (r:components<->rgb r g b rgb)
(hsv-color h s v) ;; (r:rgb-color r g b rgb)
hsv-color? ;; (r:rgb-color-r rgb r)
(h hsv-color-h) ;; (r:rgb-color-g rgb g)
(s hsv-color-s) ;; (r:rgb-color-b rgb b))
(v hsv-color-v))
(define (assert-real x)
(unless (real? x)
(error "expected real number" x)))
(define (fmod x y)
(assert-real x)
(assert-real y)
(- x (* (truncate (/ x y)) y)))
(define (rgb->hsv rgb)
(match rgb
(($ <rgb-color> r g b)
(let* ((cmax (max r g b))
(cmin (min r g b))
(delta (- cmax cmin)))
(hsv-color (cond
((= delta 0.0) 0.0)
((= cmax r)
(let ((h (* 60.0 (fmod (/ (- g b) delta) 6.0))))
(if (< h 0.0) (+ h 360.0) h)))
((= cmax g) (* 60.0 (+ (/ (- b r) delta) 2.0)))
((= cmax b) (* 60.0 (+ (/ (- r g) delta) 4.0))))
(if (= cmax 0.0)
0.0
(/ delta cmax))
cmax)))))
(define (hsv->rgb hsv)
(match hsv
(($ <hsv-color> h s v)
(let* ((h' (/ h 60.0))
(c (* v s))
(x (* c (- 1.0 (abs (- (fmod h' 2.0) 1.0)))))
(m (- v c)))
(define-values (r' g' b')
(cond
((<= 0.0 h 60.0) (values c x 0.0))
((<= h 120.0) (values x c 0.0))
((<= h 180.0) (values 0.0 c x))
((<= h 240.0) (values 0.0 x c))
((<= h 300.0) (values x 0.0 c))
((<= h 360.0) (values c 0.0 x))))
(rgb-color (+ r' m) (+ g' m) (+ b' m))))))
(define (uniform->byte x)
(inexact->exact (round (* x 255.0))))
(define (rgb->int rgb)
(match rgb
(($ <rgb-color> r g b)
(+ (* (uniform->byte r) (ash 1 16))
(* (uniform->byte g) (ash 1 8))
(uniform->byte b)))))
(define-primitive-reactive-propagator r:rgb-color rgb-color)
(define-primitive-reactive-propagator r:rgb-color-r rgb-color-r)
(define-primitive-reactive-propagator r:rgb-color-g rgb-color-g)
(define-primitive-reactive-propagator r:rgb-color-b rgb-color-b)
(define-primitive-reactive-propagator r:hsv-color hsv-color)
(define-primitive-reactive-propagator r:hsv-color-h hsv-color-h)
(define-primitive-reactive-propagator r:hsv-color-s hsv-color-s)
(define-primitive-reactive-propagator r:hsv-color-v hsv-color-v)
(define-primitive-reactive-propagator r:rgb->hsv rgb->hsv)
(define-primitive-reactive-propagator r:hsv->rgb hsv->rgb)
(define-constraint (r:components<->rgb r g b rgb)
(r:rgb-color r g b rgb)
(r:rgb-color-r rgb r)
(r:rgb-color-g rgb g)
(r:rgb-color-b rgb b))
(define-constraint (r:components<->hsv h s v hsv)
(r:hsv-color h s v hsv)
(r:hsv-color-h hsv h)
(r:hsv-color-s hsv s)
(r:hsv-color-v hsv v))
(define-constraint (r:rgb<->hsv rgb hsv)
(r:rgb->hsv rgb hsv)
(r:hsv->rgb hsv rgb))
(define (render exp) (define (render exp)
(append-child! (document-body) (sxml->dom exp))) (append-child! (document-body) (sxml->dom exp)))
(define* (slider id name min max default #:optional (step "any"))
`(div (@ (class "slider"))
(label (@ (for ,id)) ,name)
(input (@ (id ,id) (type "range")
(min ,min) (max ,max) (step ,step)
(value ,default)))))
(define (uslider id name default) ; [0,1] slider
(slider id name 0 1 default))
(define-syntax-rule (with-cells (name ...) body . body*) (define-syntax-rule (with-cells (name ...) body . body*)
(let ((name (make-cell 'name #:merge merge-ephemerals)) ...) body . body*)) (let ((name (make-cell 'name #:merge merge-ephemerals)) ...) body . body*))
(with-cells (r g b rgb h s v hsv first-name last-name full-name) (define (note-component active)
(define color (make-reactive-id 'color)) (with-cells (enabled)
(define rgb-group (list r g b)) (define t:enabled (make-transient enabled))
(define hsv-group (list h s v)) (set-trans! t:enabled #f)
(r:components<->rgb r g b rgb) (define (on-click ev)
(r:components<->hsv h s v hsv) (set-trans! t:enabled (not (get-trans t:enabled))))
(r:rgb<->hsv rgb hsv)
(define name (make-reactive-id 'name)) `(div (@ (class "note")
(event "click" ,on-click))
,(thru (active) (if active "I'm active!" "not active"))
,(thru (enabled) (if enabled "I'm enabled!" "not enabled")))))
(with-cells (first-name last-name full-name note-active)
(define name (make-reactive-id))
(define name-group (list first-name last-name)) (define name-group (list first-name last-name))
(define hex (define note (note-component note-active))
(pbind (rgb)
(list->string
(cons #\#
(let lp ((i 0) (n (rgb->int rgb)) (out '()))
(if (= i 6)
out
(lp (1+ i) (ash n -4)
(cons (integer->char
(let ((digit (logand n 15)))
(+ (if (< digit 10)
(char->integer #\0)
(- (char->integer #\a) 10))
digit)))
out))))))))
(pbind (first-name last-name -> full-name) (thru (first-name last-name -> full-name)
(string-append first-name " " last-name)) (string-append first-name " " last-name))
(render (render
`(div `(div
(h1 "Color Picker") (h1 "Propagators Playground")
(input (@ (type "text") (input (@ (type "text")
(value ,(binding name first-name #:default "vivi" #:group name-group)))) (value ,(binding (make-transient first-name name) #:default "vivi" #:group name-group))))
(input (@ (type "text") (input (@ (type "text")
(value ,(binding name last-name #:default "langdon" #:group name-group)))) (value ,(binding (make-transient last-name name) #:default "langdon" #:group name-group))))
(h2 ,full-name) (h2 ,full-name)
(div (@ (class "preview")) ,note)))
(div (@ (class "color-block")
(style ,(pbind (hex) (string-append "background-color: " hex ";")))))
(div (@ (class "hex")) ,hex))
(fieldset
(legend "RGB")
,(uslider "red" "Red"
(binding color r #:default 1.0 #:numeric? #t #:group rgb-group))
,(uslider "green" "Green"
(binding color g #:default 0.0 #:numeric? #t #:group rgb-group))
,(uslider "blue" "Blue"
(binding color b #:default 1.0 #:numeric? #t #:group rgb-group)))
(fieldset
(legend "HSV")
,(slider "hue" "Hue" 0 360 (binding color h #:numeric? #t #:group hsv-group))
,(uslider "saturation" "Saturation" (binding color s #:numeric? #t #:group hsv-group))
,(uslider "value" "Value" (binding color v #:numeric? #t #:group hsv-group))))))