Which changes to clojurescript atoms cause reagent components to re-render? - clojure

Consider the following reagent component. It uses a ref function, which updates a local state atom, based on the real size of a span element. This is done in order to re-render the component displaying its own size
(defn show-my-size-comp []
(let [size (r/atom nil)]
(fn []
(.log js/console "log!")
[:div
[:span {:ref (fn [el]
(when el (reset! size (get-real-size el))))}
"Hello, my size is:" ]
[:span (prn-str #size)]])))
If the implementation of get-real-size returns a vector, the log message is printed constantly, meaning the component unnecessarily being re-rendered all the time. If it returns just a number or a string, the log appears only twice - as intended in this scenario.
What's the reason for this? Is it maybe that updating an clojure script atom with a new vector (containing the same values though) internally means putting another JavaScript object there, thus changing the atom? Whereas putting a value produces no observable change? Just speculation...*
Anyways - for the real use case, saving the size of the span in a vector would certainly better.. Are there ways to achieve this?
I cam across this, when trying to enhance the answer given in this question.
* since in JS: ({} === {}) // false

I think I have an answer for why vector behaves differently from string/number. Reagent counts a reagent atom as "changed" (and thus updates a component that depends on it) when identical? returns false between the old and the new values. See the subhead "changed?" in this tutorial:
For ratoms, identical? is used (on the value inside the ratom) to determine if a new value has changed with regard to an old value.
However, it turns out that identical? behaves differently for vectors and for strings/ints. If you fire up either a clj or a cljs repl, you'll see that:
(identical? 1 1)
;; true
(identical? "a" "a")
;; true
(identical? [1] [1])
;; false
(identical? ["a"] ["a"])
;; false
If you look at what identical? does here, you'll see that it tests if its arguments are the same object. I think the underlying internal data representation is such that, in clojure, "a" is always the same object as itself, whereas two vectors containing the same value are not the same object as one another.
Confirmation: with ordinary rather than reagent atoms, we can see that string identity is preserved across atom resets, while vector identity is not.
(def a1 (atom "a"))
(let [aa #a1] (reset! a1 "a") (identical? aa #a1))
;; true
(def a2 (atom ["a"]))
(let [aa #a2] (reset! a2 ["a"]) (identical? aa #a2))
;; false

You can work around the problem with a not= check:
(fn [el]
(when el
(let [s (get-real-size el)]
(when (not= s #size)
(reset! size s)))))
I'm not sure what the reason is for why vectors should differ from other values.

Its rerendering like it is supposed to based on how its written. You are derefing the atom in the same function as you are resetting it. I always keep these separate.
(defn span-size [size]
[:span (prn-str #size)])
(defn show-my-size-comp []
(let [size (r/atom nil)]
(fn []
(.log js/console "log!")
[:div
[:span {:ref (fn [el]
(when el (reset! size (get-real-size el))))}
"Hello, my size is:"]
[span-size]])))

Related

Extend swap! behaviour for nil type in Clojure

let suppose, I have var atom-var
(def atom-val (atom []))
Also suppose a standard behaviour of atom:
(swap! atom-val conj {:b "2"})
=> #object[clojure.lang.Atom 0x6500d3fd {:status :ready, :val [{:a "2"}]
(#atom-val)
=> #object[clojure.lang.Atom 0x6500d3fd {:status :ready, :val [{:a "2"}]
I want to create the same behaviour as it would work with nil object, but without actions:
(def atom-val nil)
(swap! atom-val conj "New val")
Of course, I will get a NullPointerException. But I want that nothing happened, supress it. I do not need to write try every time it, I just need the described behavuiour.
I see that swap! is a function, atom is a function, atom returns clojure.lang.IAtom, clojure.lang.IAtom is an interface. I cannot extend interface.
How can I get the described behaviour?
Well, I have a global dynamic variable which is equal to nil
(def ^:dynamic atom-val nil).
Whenever a thread is created (it's ring handler with compojure), I am binding atom-val to
(defn func [handler]
(fn [request]
(binding [atom-val (atom [])]
(handler request)
)
)
So, I have such a form in different functions:
(swap! atom-val conj "New val").
I can run it everywhere lots of times (inside/outside different functions). It's really bad to check every time whether atom-val is null or not. Functions have to make swap!, but sometimes atom-val cannot be initialized properly (when a function makes swap! outside ring handlers, before binding).
So I decided to do it this way: I'd like to extend swap! protocol for Atom and when nil (when atom-val is nil) is passed it mustn't throw NullPointerException.
If you want an atom that does nothing, you can write:
(def noop-atom
(reify clojure.lang.IAtom
(reset [_ _])
(swap [_ _])
(swap [_ _ _])
(swap [_ _ _ _])
(swap [_ _ _ _ _])
(compareAndSet [_ _ _])))
You can then use this atom as the root-value of the dynamic var.
If your goal is to manage state during the lifecycle of a Ring request/response, you can write a custom middleware.
You need to use fnil. See https://clojuredocs.org/clojure.core/fnil
Example:
(def atom-val nil)
(def nil-swap! (fnil swap! (atom [])))
(nil-swap! atom-val conj "New val") => ["New val"]
And don't forget to keep a browser tab open to The Clojure CheatSheet at all times!
So, you want to swap! on a nil? I'm not sure what you meant with actions and doing nothing.
It definitely doesn't make sense (at least to me) to swap! a nil value; if it does to you, you should review what atoms really are. Perhaps you mean that when given a nil instead of an atom reference the swap! wouldn't do anything.
If so then you can just make your own function to do that check for you:
(defn nilable-swap!
[a f]
(when a (swap! a f)))
But I really don't recommend you do that, if this is what you want to do, it's an indication of bad design and control flow. Of course, it does make sense to check if your atom reference is nil if you're not sure, but do you really need to routinely check that? is there truly no point in which you know you got your atom reference?
I think I might have answered your question but if I misunderstood, feel free to clarify and I'll update/delete the answer.

Clojure: How to determine if a nested list contains non-numeric items?

I need to write a Clojure function which takes an unevaluated arbitrarily deep nesting of lists as input, and then determines if any item in the list (not in function position) is non-numeric. This is my first time writing anything in Clojure so I am a bit confused. Here is my first attempt at making the function:
(defn list-eval
[x]
(for [lst x]
(for [item lst]
(if(integer? item)
(println "")
(println "This list contains a non-numeric value")))))
I tried to use a nested for-loop to iterate through each item in every nested list. Trying to test the function like so:
=> (list-eval (1(2 3("a" 5(3)))))
results in this exception:
ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn listeval.core/eval7976 (form-init4504441070457356195.clj:1)
Does the problem here lie in the code, or in how I call the function and pass an argument? In either case, how can I make this work as intended?
This happens because (1 ..) is treated as calling a function, and 1 is a Long, and not a function. First you should change the nested list to '(1(2 3("a" 5(3)))). Next you can change your function to run recursively:
(defn list-eval
[x]
(if (list? x)
(for [lst x] (list-eval lst))
(if (integer? x)
(println "")
(println "This list contains a non-numeric value"))))
=> (list-eval '(1(2 3("a" 5(3)))))
There is a cool function called tree-seq that does all the hard work for you in traversing the structure. Use it then remove any collections, remove all numbers, and check if there is anything left.
(defn any-non-numbers?
[x]
(->> x
(tree-seq coll? #(if (map? %) (vals %) %))
(remove (some-fn coll? number?))
not-empty
boolean))
Examples:
user=> (any-non-numbers? 1)
false
user=> (any-non-numbers? [1 2])
false
user=> (any-non-numbers? [1 2 "sd"])
true
user=> (any-non-numbers? [1 2 "sd" {:x 1}])
true
user=> (any-non-numbers? [1 2 {:x 1}])
false
user=> (any-non-numbers? [1 2 {:x 1 :y "hello"}])
true
If you want to consider map keys as well, just change (vals %) to (interleave (keys %) (vals %)).
quoting
As others have mentioned, you need to quote a list to keep it from being evaluated as
code. That's the cause of the exception you're seeing.
for and nesting
for will only descend to the nesting depth you tell it to. It is not a for loop,
as you might expect, but a sequence comprehension, like the the python list comprehension.
(for [x xs, y ys] y) will presume that xs is a list of lists and flatten it.
(for [x xs, y ys, z zs] z) Is the same but with an extra level of nesting.
To walk down to any depth, you'd usually use recursion.
(There are ways to do this iteratively, but they're more difficult to wrap your head around.)
side effects
You're doing side effects (printing) inside a lazy sequence. This will work at the repl,
but if you're not using the result anywhere, it won't run and cause great confusion.
It's something every new clojurian bumps into at some point.
(doseq is like for, but for side effects.)
The clojure way is to separate functions that work with values from functions that
"do stuff", like printing to the console of launching missiles, and to keep the
side effecting functions as simple as possible.
putting it all together
Let's make a clear problem statement: Is there a non number anywhere inside an
arbitrarily nested list? If there is, print a message saying that to the console.
In a lot of cases, when you'd use a for loop in other langs reduce is what you want in clojure.
(defn collect-nested-non-numbers
;; If called with one argument, call itself with empty accumulator
;; and that argument.
([form] (collect-nested-non-numbers [] form))
([acc x]
(if (coll? x)
;; If x is a collection, use reduce to call itself on every element.
(reduce collect-nested-non-numbers acc x)
;; Put x into the accumulator if it's a non-number
(if (number? x)
acc
(conj acc x)))))
;; A function that ends in a question mark is (by convention) one that
;; returns a boolean.
(defn only-numbers? [form]
(empty? (collect-nested-non-numbers form)))
;; Our function that does stuff becomes very simple.
;; Which is a good thing, cause it's difficult to test.
(defn warn-on-non-numbers [form]
(when-not (only-numbers? form)
(println "This list contains a non-numeric value")))
And that'll work. There already exists a bunch of things that'll help you walk a nested structure, though, so you don't need to do it manually.
There's the clojure.walk namespace that comes with clojure. It's for when you have
a nested thing and want to transform some parts of it. There's tree-seq which is explained
in another answer. Specter is a library which is
a very powerful mini language for expressing transformations of nested structures.
Then there's my utils library comfy which contains reduce versions of the
functions in clojure.walk, for when you've got a nested thing and want to "reduce" it to a single value.
The nice thing about that is that you can use reduced which is like the imperative break statement, but for reduce. If it finds a non-number it doesn't need to keep going through the whole thing.
(ns foo.core
(:require
[madstap.comfy :as comfy]))
(defn only-numbers? [form]
(comfy/prewalk-reduce
(fn [ret x]
(if (or (coll? x) (number? x))
ret
(reduced false)))
true
form))
Maybe by "any item in the list (not in function position)" you meant this?
(defn only-numbers-in-arg-position? [form]
(comfy/prewalk-reduce
(fn [ret x]
(if (and (list? x) (not (every? (some-fn number? list?) (rest x))))
(reduced false)
ret))
true
form))

For a function that updates a world "state", I want to return a vector of strings of events that happened

I have this small game world state, something like the following:
(defn odds [percentage]
(< (rand-int 100) percentage))
(defn world []
{:entities []})
(defn make-bird []
{:pos [(rand-int 100) (rand-int 100)]
:age 0
:dir (vec/dir (rand (. Math PI)))})
(defn generate-entities [entities]
(if (odds 10)
(conj entities (make-bird))
entities))
(defn update-entity [entity]
(-> entity
(update :pos (partial vec/add (:dir entity)))
(update :age inc)))
(defn update-entities [entities]
(vec (map update-entity entities)))
(defn old? [{age :age}]
(> age 10))
(defn prune-entities [entities]
(vec (filter #(not (old? %)) entities)))
(defn update-world [world]
(-> world
(update :entities generate-entities)
(update :entities update-entities)
(update :entities prune-entities)))
So update-world goes through three steps. First there's a 1/10 chance of generating a new bird entity, which flies in a random direction. Then it updates all birds, updating their position and incrementing their age. Then it prunes all old birds.
I use this same technique for generating particles systems. You can do fun stuff like (iterate update-world (world)) to get a lazy list of world states which you can consume at whatever frame rate you want.
However, I now have a game world with autonomous entities which roam around and do stuff, kind of like the birds. But I want to get a textual representation of what happened when evaluating update-world. For example, update-world would ideally return a tuple of the new world state and a vector of strings - ["A bird was born at [12, 8].", "A bird died of old age at [1, 2]."].
But then I really can't use (iterate update-world (world)) anymore. I can't really see how to do this.
Is this something you'd use with-out-string for?
If you want to enhance only your top-level function (update-world) in your case you can just create a wrapper function that you can use in iterate. A simple example:
(defn increment [n]
(inc n))
(defn logging-increment [[_ n]]
(let [new-n (increment n)]
[(format "Old: %s New: %s" n new-n) new-n]))
(take 3 (iterate logging-increment [nil 0]))
;; => ([nil 0] ["Old: 0 New: 1" 1] ["Old: 1 New: 2" 2])
In case you want to do it while collecting data at multiple level and you don't want to modify the signatures of your existing functions (e.g. you want to use it only for debugging), then using dynamic scope seems like a reasonable option.
Alternatively you can consider using some tracing tools, like clojure/tools.trace. You could turn on and off logging of your function calls by simply changing defn to deftrace or using trace-ns or trace-vars.
There are two potential issues with using with-out-str
It returns a string, not a vector. If you need to use a vector, you'll need to use something else.
Only the string is returned. If you are using with-out-str to wrap a side-effect (e.g., swap!), this might be fine.
For debugging purposes, I usually just use println. You can use with-out if you want control over where the output goes. You could even implement a custom stream that collects the output into a vector of strings if you wanted. You could get similar results with a dynamically bound vector that you accumulate (via set!) the output string (or wrap the vector in an atom and use swap!).
If the accumulated vector is part of the computation per se, and you want to remain pure, you might consider using a monad.
What about using clojure.data/diff to generate the string representation of changes? You could do something like this:
(defn update-world [[world mutations]]
(let [new-world (-> world
(update :entities generate-entities)
(update :entities update-entities)
(update :entities prune-entities))]
[new-world (mutations (clojure.data/diff world new-world))]))
Then you could do something like (iterate update-world [(world) []]) to get the ball rolling.

ClojureScript - convert arbitrary JavaScript object to Clojure Script map

I am trying to convert a Javascript object to a Clojure. However, I get the following error :
(js/console.log (js->clj e)) ;; has no effect
(pprint (js->clj e)) ;; No protocol method IWriter.-write defined for type object: [object Geoposition]
Yes, this object comes from the Geolocation API. I suppose that I have to extend IEncodeClojure and IWriter, but I have no clue how.
For instance adding the following :
(extend-protocol IEncodeClojure
Coordinates
(-js->clj [x options]
(println "HERE " x options)))
Yields an error when loading my code : Uncaught TypeError: Cannot read property 'prototype' of undefined
The accepted answer wasn't working for me with the javascript object window.performance.timing. This is because Object.keys() doesn't actually return the props for the PerformanceTiming object.
(.keys js/Object (.-timing (.-performance js/window))
; => #js[]
This is despite the fact that the props of PerformanceTiming are indeed iterable with a vanilla JavaScript loop:
for (a in window.performance.timing) {
console.log(a);
}
// navigationStart
// unloadEventStart
// unloadEventEnd
// ...
The following is what I came up with to convert an arbitrary JavaScript object to a ClojureScript map. Note the use of two simple Google Closure functions.
goog.typeOf wraps typeof, which isn't normally accessible to us in ClojureScript. I use this to filter out props which are functions.
goog.object.getKeys wraps for (prop in obj) {...}, building up an array result which we can reduce into a map.
Solution (flat)
(defn obj->clj
[obj]
(-> (fn [result key]
(let [v (goog.object/get obj key)]
(if (= "function" (goog/typeOf v))
result
(assoc result key v))))
(reduce {} (.getKeys goog/object obj))))
Solution (recursive)
Update: This solution will work for nested maps.
(defn obj->clj
[obj]
(if (goog.isObject obj)
(-> (fn [result key]
(let [v (goog.object/get obj key)]
(if (= "function" (goog/typeOf v))
result
(assoc result key (obj->clj v)))))
(reduce {} (.getKeys goog/object obj)))
obj))
js->clj only works for Object, anything with custom constructor (see type) will be returned as is.
see: https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/core.cljs#L9319
I suggest doing this instead:
(defn jsx->clj
[x]
(into {} (for [k (.keys js/Object x)] [k (aget x k)])))
UPDATE for correct solution see Aaron's answer, gotta use goog.object
Two approaches that do not require writing custom conversion functions - they both employ standard JavaScript functions to loose the custom prototype and thus enable clj->js to work correctly.
Using JSON serialization
This approach just serializes to JSON and immediately parses it:
(js->clj (-> e js/JSON.stringify js/JSON.parse))
Advantages:
does not require any helper function
works for nested objects, with/without prototype
supported in every browser
Disadvantages:
performance might be a problem in critical pieces of codebase
will strip any non-serializable values, like functions.
Using Object.assign()
This approach is based on Object.assign() and it works by copying all the properties from e onto a fresh, plain (no custom prototype) #js {}.
(js->clj (js/Object.assign #js {} e))
Advantages:
does not require any helper function
Disadvantages:
works on flat objects, if there is another nested object withing e, it won't be converted by clj->js.
Object.assign() is not supported by old browsers, most notably - IE.
(defn obj->clj
([obj]
(obj->clj obj :keywordize-keys false))
([obj & opts]
(let [{:keys [keywordize-keys]} opts
keyfn (if keywordize-keys keyword str)]
(if (and (not-any? #(% obj) [inst? uuid?])
(goog.isObject obj))
(-> (fn [result k]
(let [v (goog.object/get obj k)]
(if (= "function" (goog/typeOf v))
result
(assoc result (keyfn k) (apply obj->clj v opts)))))
(reduce {} (.getKeys goog/object obj)))
obj))))
Small problem with the original above is that JS treats #inst and #uuid as objects. Seems like those are the only tagged literals in clojure
I also added the option to keywordize keys by looking at js->clj source

Why in this example calling (f arg) and calling the body of f explicitly yields different results?

First, I have no experience with CS and Clojure is my first language, so pardon if the following problem has a solution, that is immediately apparent for a programmer.
The summary of the question is as follows: one needs to create atoms at will with unknown yet symbols at unknown times. My approach revolves around a) storing temporarily the names of the atoms as strings in an atom itself; b) changing those strings to symbols with a function; c) using a function to add and create new atoms. The problem pertains to step "c": calling the function does not create new atoms, but using its body does create them.
All steps taken in the REPL are below (comments follow code blocks):
user=> (def atom-pool
#_=> (atom ["a1" "a2"]))
#'user/atom-pool
'atom-pool is the atom that stores intermediate to-be atoms as strings.
user=> (defn atom-symbols []
#_=> (mapv symbol (deref atom-pool)))
#'user/atom-symbols
user=> (defmacro populate-atoms []
#_=> (let [qs (vec (remove #(resolve %) (atom-symbols)))]
#_=> `(do ~#(for [s qs]
#_=> `(def ~s (atom #{}))))))
#'user/populate-atoms
'populate-atoms is the macro, that defines those atoms. Note, the purpose of (remove #(resolve %) (atom-symbols)) is to create only yet non-existing atoms. 'atom-symbols reads 'atom-pool and turns its content to symbols.
user=> (for [s ['a1 'a2 'a-new]]
#_=> (resolve s))
(nil nil nil)
Here it is confirmed that there are no 'a1', 'a2', 'a-new' atoms as of yet.
user=> (defn new-atom [a]
#_=> (do
#_=> (swap! atom-pool conj a)
#_=> (populate-atoms)))
#'user/new-atom
'new-atom is the function, that first adds new to-be atom as string to `atom-pool. Then 'populate-atoms creates all the atoms from 'atom-symbols function.
user=> (for [s ['a1 'a2 'a-new]]
#_=> (resolve s))
(#'user/a1 #'user/a2 nil)
Here we see that 'a1 'a2 were created as clojure.lang.Var$Unbound just by defining a function, why?
user=> (new-atom "a-new")
#'user/a2
user=> (for [s ['a1 'a2 'a-new]]
#_=> (resolve s))
(#'user/a1 #'user/a2 nil)
Calling (new-atom "a-new") did not create the 'a-new atom!
user=> (do
#_=> (swap! atom-pool conj "a-new")
#_=> (populate-atoms))
#'user/a-new
user=> (for [s ['a1 'a2 'a-new]]
#_=> (resolve s))
(#'user/a1 #'user/a2 #'user/a-new)
user=>
Here we see that resorting explicitly to 'new-atom's body did create the 'a-new atom. 'a-new is a type of clojure.lang.Atom, but 'a1 and 'a2 were skipped due to already being present in the namespace as clojure.lang.Var$Unbound.
Appreciate any help how to make it work!
EDIT: Note, this is an example. In my project the 'atom-pool is actually a collection of maps (atom with maps). Those maps have keys {:name val}. If a new map is added, then I create a corresponding atom for this map by parsing its :name key.
"The summary of the question is as follows: one needs to create atoms at will with unknown yet symbols at unknown times. "
This sounds like a solution looking for a problem. I would generally suggest you try another way of achieving whatever the actual functionality is without generating vars at runtime, but if you must, you should use intern and leave out the macro stuff.
You cannot solve this with macros since macros are expanded at compile time, meaning that in
(defn new-atom [a]
(do
(swap! atom-pool conj a)
(populate-atoms)))
populate-atoms is expanded only once; when the (defn new-atom ...) form is compiled, but you're attempting to change its expansion when new-atom is called (which necessarily happens later).
#JoostDiepenmaat is right about why populate-atoms is not behaving as expected. You simply cannot do this using macros, and it is generally best to avoid generating vars at runtime. A better solution would be to define your atom-pool as a map of keywords to atoms:
(def atom-pool
(atom {:a1 (atom #{}) :a2 (atom #{})}))
Then you don't need atom-symbols or populate-atoms because you're not dealing with vars at compile-time, but typical data structures at run-time. Your new-atom function could look like this:
(defn new-atom [kw]
(swap! atom-pool assoc kw (atom #{})))
EDIT: If you don't want your new-atom function to override existing atoms which might contain actual data instead of just #{}, you can check first to see if the atom exists in the atom-pool:
(defn new-atom [kw]
(when-not (kw #atom-pool)
(swap! atom-pool assoc kw (atom #{}))))
I've already submitted one answer to this question, and I think that that answer is better, but here is a radically different approach based on eval:
(def atom-pool (atom ["a1" "a2"]))
(defn new-atom! [name]
(load-string (format "(def %s (atom #{}))" name)))
(defn populate-atoms! []
(doseq [x atom-pool]
(new-atom x)))
format builds up a string where %s is substituted with the name you're passing in. load-string reads the resulting string (def "name" (atom #{})) in as a data structure and evals it (this is equivalent to (eval (read-string "(def ...)
Of course, then we're stuck with the problem of only defining atoms that don't already exist. We could change the our new-atom! function to make it so that we only create an atom if it doesn't already exist:
(defn new-atom! [name]
(when-not (resolve (symbol name))
(load-string (format "(def %s (atom #{}))" name name))))
The Clojure community seems to be against using eval in most cases, as it is usually not needed (macros or functions will do what you want in 99% of cases*), and eval can be potentially unsafe, especially if user input is involved -- see Brian Carper's answer to this question.
*After attempting to solve this particular problem using macros, I came to the conclusion that it either cannot be done without relying on eval, or my macro-writing skills just aren't good enough to get the job done with a macro!
At any rate, I still think my other answer is a better solution here -- generally when you're getting way down into the nuts & bolts of writing macros or using eval, there is probably a simpler approach that doesn't involve metaprogramming.