This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
- # architecture (3)
- # beginners (41)
- # boot (7)
- # cider (16)
- # cljs-dev (8)
- # cljsrn (2)
- # clojure (214)
- # clojure-austin (4)
- # clojure-russia (52)
- # clojure-spec (8)
- # clojure-taiwan (1)
- # clojure-uk (10)
- # clojurescript (87)
- # cursive (14)
- # datascript (34)
- # datomic (11)
- # dirac (55)
- # emacs (12)
- # hoplon (44)
- # luminus (6)
- # lumo (24)
- # off-topic (1)
- # om (8)
- # onyx (7)
- # overtone (2)
- # pedestal (1)
- # protorepl (4)
- # re-frame (7)
- # reagent (1)
- # ring (4)
- # rum (2)
- # slack-help (1)
- # spacemacs (2)
- # specter (32)
- # unrepl (131)
- # untangled (14)
- # yada (3)
I've been trying to use Specter to transform and filter data structures that I'm going to use in my UI. So far the state management functions are performing great, even within Javelin formula cells. But I ran into a wall 😬
Seems like any formula cells that rely on Specter code for their output will not update reactively in the dom. They render their initial values properly on pageload. Printing their results to the console works fine in the normal reactive way.
When I define a formula cell with specter code in the formula. I can't use the
(cell=) macros, getting the error "formula expansion contains unsupported def form" But by using the
(formula) fn It seemed to work.
Here is my test code:
I'm using UI for the rendering code. But I imagine it would be the same errors with vanilla Hoplon (should test this tho)
(defc seed ) (defc= sum (apply + seed)) ; (defc= transformula (s/transform [s/ALL] inc seed)) ; doesn't compile, error: "formula expansion contains unsupported def form")) ; (defn transformula  ; (cell= (s/transform [s/ALL] inc seed))) ; doesn't compile, same error as above)) (defn transformula  ( (formula #(s/transform [s/ALL] inc %)) seed)) ; (defc= trans-sum (apply + @(transformula))) ;;; doesn't update in console output (defn trans-sum  ( (formula #(apply + %)) @(transformula))) (cell= (do (.clear js/console) (prn seed) (prn sum) (prn @(transformula)) (prn @(trans-sum)))) ;;; all functions update properly in console (elem :p 20 :gv 10 (elem :sh (r 1 1) :click #(reset! seed (conj @seed 1)) "seed: " (cell= (str seed))) (elem :sh (r 1 1) :click #(reset! seed ) "sum: " sum) (elem :sh (r 1 1) "transformula: " ; @(transformula) ; doesn't update in dom ; (cell= (str @(transformula))))) ; doesn't update in dom @((formula #(str %)) (transformula))) ; still doesn't update in dom (elem :sh (r 1 1) :click #(reset! seed ) "trans-sum: " @(trans-sum))) ;doesnt update in dom, same as above...
Specter uses macros (https://github.com/nathanmarz/specter/wiki/List-of-Macros), and has some kind of caching mechanism to optimize performance (path precompilation, etc). https://github.com/nathanmarz/specter/wiki/Specter's-inline-caching-implementation https://github.com/nathanmarz/specter/wiki/Specter-0.11.0:-Performance-without-the-tradeoffs. I hope that's not a dealbreaker to use with Hoplon. I would like to use them both together. I appreaciate Javelin's bottom-up state-building reactive ways. But I'm fond of Specter's top-down declarative data wizardry. I understand the Clojure experts prefer not too much magic, but I feel empowered by it, as a lot of "simple" data-structure wrangling code still looks like Klingon to me 🙃 Thanks for any insight!
I got it updating in the dom!! seems there were some unnecessary derefs in the mix.
But I'm still a bit confused why
(defc seed ) (defn transformula  ( (formula #(transform [ALL] inc %)) seed)) (cell= (do (.clear js/console) (prn seed) (prn @(transformula)))) (elem :p 20 :gv 10 (elem :sh (r 1 1) :click #(swap! seed conj 1) "seed: " (cell= seed)) (elem :sh (r 1 1) "transformula: " (transformula)))
(transformula)has to be derefed to print to console, even though it is wrapped in
(cell= (prn @(transformula)prints and updates normally And why if
@(transformula)is not required in an
(elem)and doesn't update in the dom when derefed there. And why
(elem (cell= @(transformula)does not update in the dom, like it does in the console?
Looks like if I don't define the Specter function within a formula cell, then I can use the Javelin formula cell macros. This is working:
(defc seed ) (defn specter-trans [v] (transform [ALL] inc v)) (defc= transformula (specter-trans seed)) (cell= (do (.clear js/console) (prn seed) (prn transformula))) (elem :p 20 :gv 10 (elem :sh (r 1 1) :click #(swap! seed conj 1) "seed: " seed)) (elem :sh (r 1 1) "transformula: " transformula)) (elem :sh (r 1 1) "transformula in another formula: " (cell= (conj transformula 100))
Yes, I've been using it, It was the only thing that let me code Specter functions directly in a formula cell. If I did
(cell= (s/fn ...)) in would say "formula expansion contains unsupported def form"
That got me going. But it made the code a lot messier, and I had to keep using
(formula..) when composing values in the
(elems) or they wouldn't update reactively.
i can see how you got around it by making a function of your own, to "hide" the macro
Yes, it is a macro. It seems like things are working now, as long as I define my specter functions outside of formulae. And don't get tripped up by the subtleties where/when to apply deref.
if the clj versino of specter is any indication, there is a function directly underneath
(transform [ALL] inc [1 2 3]) expands to
(com.rpl.specter.impl/compiled-transform* (com.rpl.specter/path [com.rpl.specter/ALL]) inc [1 2 3])
which means you could do i.e.
(defn transform-cell [& args] (apply (formula compiled-transform*) args))
hopefully there is some clear, de-optimized way to call a function that does the specter things
in this case i'd say its mostly javelin to blame, because cell= needs to fully expand its arguments. walk all code
Well defining the Specter function on it's own, then invoking it in a formula cell seems to be working like normal.
(defn specter-fn [v} ....) (defc= transformula (specter-fn v)) (transformula)
It just seems like the derefing is a bit inconsistent, I guess cause of the macro stuff
Is there any difference btw
(defn foo  (cell= (fn ...)) and
(def foo (cell= (fn ...))?
Maybe that's what was going on. I was using
(defn ) to wrap the formula. Perhaps that's why things weren't updating in the dom as expected, but would print ok to the console.
So If I want a cannonical, reactive formula cell to reference in my ui, and I need to wrap the definition of it, I should use the
Seems to work as expected. Composes in the elems!!! @alandipert Thank you for your help with macro/interop intuition. Now I wont feel so helpless if a macro borks on the app level.
(def transformula ( (formula #(transform [ALL] inc %)) seed))
@chromalchemy nice! and yeah, it's good in general to avoid situations where you're creating cells dynamically
ideally your app has a static number of cells, and the app state is represented as values in those cells
per wise you risk leaking cells and slowing down your app, unless you develop rules for creating them or deallocating. like what for-tpl etc do