(def s (r/atom 0))
(defn foo []
[:input {:type "text"
:value @s
:on-click #(reset! s (.-target.value %)}]) ; unstable closure reference
(defn change [e] (reset! s (.-target.value e)) ; you're suggesting we name it
(defn foo2 []
[:input {:type "text"
:value @s
:on-click change}]) ; which works here - this is now a stable function reference. Good!
; But what if the named function is not static, but a closure
(defn foo3 [something]
(let [change' (fn [e] ; closure instance is rebuilt each render
(if something
(reset! s (.-target.value e))))]
[:input {:type "text"
:value @s
:on-click change'}])) ; unstable closure reference
The answer generally is to do what you say and hoist up any closures to static scope, like foo2 here.
Even foo3
can be written into a hoisted static function with additional parameters, and use reagent.core/partial
instead of clojure.core/partial
to preserve stabiltiy. But it is not always reasonable and it can cause great contortions in your code when you're basically no longer allowed to write (fn [])
ever again.
Ya, I think that's where I meant, I don't really know what React is trying to achieve exactly here, so if two functions that close over different values shouldn't be equal this won't work. But, then I don't understand how it works for top level functions with parameters either.
Like:
The on-click closure above, is no different from:
Well I don't see what is different if it is, in that, what variables it closes over is static, but ya, the value captured could be different. OH, okay it just clicked in my brain. Are we saying that when ClojureScript closes over a variable, it actually copies the value in it into the closed function? I guess I never really thought about that. Here I assumed it copied the local variable, thus it would still contextually resolve the value in it when it runs, and as such, I thought of it much more like a parameter.
Hum, ok. Now I wonder, could a macro be written that is like
fn
but refuses to close over things? Like detects a closure and throws at compile time. Now if you used that in combination with what I mentioned above, it would be a great reminder to never close over a value, but instead take a parameter or use the partial from reagent.