Bogdanp / Bogdanp/racket-gui-easy
Make custom observables (e.g., parameters)
- Dominant language
- Racket
- Stars
- 153
- Forks
- 18
- PR merge metrics
- No merged PRs in 30d
Description
I have a use-case where I would like to turn Racket parameters into observables. I can wrap the parameter in an observable, and then manage all the patterns of using it myself, but I managed to extract those patterns. The trouble is, I can't create my own thing and tell racket/gui/easy that it should count as observable and how it behaves under all the observable operations. The best I can do currently is provide a set of macros that I call `obsp` for observable parameter. Now the management is hidden away, but I still have to remember the right thing to call (`<~` or `<~p`?).
The fundamental reason for this is that my custom observable parameter is really three values: the parameter itself, an observable of the parameter, and a derived observable that uses the parameter's value. This third one is what we are usually concerned with when reading, and the value we want to use when writing via an update function, but we can only do the writes through the second value.
If there were a `prop:` or `gen:` interface I could implement on a struct and have it co-operate with all of racket/gui/easy's observable procedures, that would be my ideal world. As it is, here's the set of macros for observable parameters (they essentially hide the second value from you, though it is accessible; in my examples, it would be `@internal-@op`).
```racket
#lang racket
(require racket/gui/easy
racket/gui/easy/operator
syntax/parse/define
(for-syntax racket/syntax))
(define-for-syntax (@internal stx id)
(format-id stx "@internal-~a" id #:source stx))
(define-syntax-parse-rule (define/obsp n:id p:expr)
#:with @internal (@internal #'n (syntax-e #'n))
(begin
(define/obs @internal p)
(define/obs n (@internal . ~> . (λ (the-p) (the-p))))))
(define (internal-obsp-update! internal o f)
(internal . <~ . (λ (the-p) (the-p (f (the-p))) the-p))
(obs-peek o))
(define-syntax-parse-rule (obsp-update! o:id f:expr)
#:with @internal (@internal #'o (syntax-e #'o))
(internal-obsp-update! @internal o f))
;; don't need p~>/obsp-map because ~>/obs-map already work
(define-syntax <~p (make-rename-transformer #'obsp-update!))
(define-syntax-parse-rule (:=p o:id v:expr) (<~p o (const v)))
(define-syntax-parse-rule (λ:=p o:id f:expr) (λ (v) (:=p o (f v))))
(define-syntax-parse-rule (λ<~p o:id f:expr) (thunk (<~p o f)))
(define p (make-parameter #f))
(define/obsp @op p)
(obs-observe! @op (λ (x) (printf "op: Value: Got ~s~n" x)))
(equal? (obs-peek @op) (p))
;; bypass @op
(p 1)
;; new versions
(obsp-update! @op add1) ;; 2
(<~p @op add1) ;; 3
(:=p @op 4) ;; 4
((λ:=p @op add1) (obs-peek @op)) ;; 5
((λ<~p @op add1)) ;; 6
```
At least `:=p` looks cute…
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.