Pretty new to clojure so be kind.
(def state {:currentPosition "1"})
(while (not= (state :currentPosition) "2") ; this is true
(assoc state :currentPosition "2") ;
)
This causes an infinite loop and it wont run but I don't understand why. The condition
(not= (state :currentPosition) "2")
is true
therefore, the loop starts, then inside the loop, I am updating the state. Why doesn't the loop end and just stalls light table? Thanks
Clojure datastructures are immutable so (assoc state :currentPosition "2") doesn't mutate the object assigned to state var but just returns a new version of a map.
If you want to modify values in your vars you need to wrap them in one of Clojure's refs. You could for example use atom:
(def state (atom {:currentPosition 1}))
(reset! state {:currentPosition 2})
#state ;; => {:currentPosition 2}
(swap! state update :currentPosition inc)
#state ;; => {:currentPosition 3}
Related
According to Clojure's documentation, reset! can be used as:
Sets the value of atom to newval without regard for the current value.
Returns newval.
Thus, I can do:
user> (def my-test (atom 666))
#'user/my-test
user> my-test
#<Atom#66d7a880: 666>
user> #my-test
666
user> (reset! my-test 77)
77
user> my-test
#<Atom#66d7a880: 77>
user> #my-test
77
But, is there any difference between using another def instead of reset!?
user> (def my-test (atom 666))
#'user/my-test
user> my-test
#<Atom#66d7a880: 666>
user> #my-test
666
user> (reset! my-test 77)
77
user> my-test
#<Atom#66d7a880: 77>
user> #my-test
77
;;;; converting it back to the original value via def
user> (def my-test (atom 666))
#'user/my-test
user> #my-test
666
user> my-test
#<Atom#7ce4f432: 666>
user>
Just by reading the experiments on the REPL I cannot identify any difference. But I am new to Clojure, so I am probably naive here.
If there is any difference, why should I use reset! instead of a new def?
You can see the answer in the REPL output in your question. When you write (reset! a 1), you give a new value to the existing atom. When you write (def a (atom 1)), you get a brand new atom. Why does this matter? Because someone may have another reference to the old atom: in the former case they see the new value, and in the latter case they don't. Compare, for example:
(def a (atom 0))
(defn counter [c] (fn [] (swap! c inc)))
(def count-up (counter a))
(count-up) ; 1
(count-up) ; 2
(reset! a 0)
(count-up) ; 1 again
with
(def a (atom 0))
(defn counter [c] (fn [] (swap! c inc)))
(def count-up (counter a))
(count-up) ; 1
(count-up) ; 2
(def a (atom 0))
(count-up) ; 3, because the old atom still holds 2
Changes to atoms are always free of race conditions. New-def-ing is not.
A Clojure Var is meant to be a global value that, in general, never changes (as always, there are exceptions to every rule). As an example, function declarations are normally stored in a Var.
A Clojure Atom is meant to point to a value that can change. An atom may be held in a global Var or a local variable binding (e.g. in a (let ...) form). Atoms are thread-safe (this is one of their primary purposes).
If you are just playing around with experimental code with only one thread, you can do a lot of sloppy or dangerous stuff and there is no problem. However, you should learn how to use each tool for its intended purpose.
More detailed discussion:
Brave Clojure
Book Getting Clojure
Clojure.org - Vars
Clojure.org - Atoms
clojuredocs.org - atom
Clojure CheatSheet
def creates a new atom (means allocate new memory space for an atom - setting it up - setting a pointer), while reset! just resets an existing atom (just changing value in the cell the pointer points to).
Therefore it is logical that reset! must be much cheaper (faster execution and less usage of resources) than def which you can test by:
(def n 10000000)
(time (dotimes [_ n] (def a (atom 1))))
## "Elapsed time: 2294.676443 msecs"
(def b (atom 1))
(time (dotimes [_ n] (reset! b 1)))
## "Elapsed time: 106.03302 msecs"
So reset! is one magnitude of order faster than def.
I am trying to represent gui elements in Clojure/Script. The problem is that the elements are nested and need to communicate with each other. I thought to pass references to each other, but it seems I can't just pass mutual references to each other, as I naively thought.
Code I tried
(defrecord Element [position size parent children state type value])
(def parent (atom (Element. ...)))
(def child (atom (Element. ...)))
(assoc #parent :child child)
(assoc #child :parent parent)
this doesn't work.
What is the idiomatic solution in Clojure for this kind of problem?
Update: mutual referencing doesn't seem to work:
(defrecord Element [position size parent children state type value])
(def parent (atom (Element. {:x 0 :y 0} {:width 1600 :height 900} nil nil :collapsed :container nil)))
(def child (atom (Element. {:x 5 :y 5} {:width 100 :height 100} nil nil :collapsed :container nil)))
(swap! parent assoc :child child)
(swap! child assoc :parent parent)
#object[InternalError InternalError: too much recursion]
cljs.core.PersistentArrayMap.prototype.cljs$core$ILookup$_lookup$arity$2 (jar
:file:/C:/Users/user/.m2/repository/org/clojure/clojurescript/1.10.520/clojuresc
ript-1.10.520.jar!/cljs/core.cljs:6811:10)
Maybe in Clojure you don't do mutual references?
You're associating the parent to the child, but then throwing away the new Element. You need to set the value in the atom using reset! or swap!:
(swap! parent assoc :child child) ; Equivalent to (swap! parent #(assoc % :child child))
(swap! child assoc :parent parent)
swap! is similar to update, but it replaces the value held within an atom.
Also note, you gave a field the name children, but in your example, you're referring to it using :child. children suggests multiple children though, so I'd change it to start as a vector instead of nil, then conj to it. I'd also create a new-element helper:
(defrecord Element [position size parent children state type value])
(defn new-element [position size]
(->Element position size nil [] :collapsed :container nil))
(def parent (atom (new-element {:x 0 :y 0} {:width 1600 :height 900})))
(def child (atom (new-element {:x 5 :y 5} {:width 100 :height 100})))
(swap! parent update :children conj child)
(swap! child assoc :parent parent)
To address the error that you're getting, it runs fine for me in Clojure.
If I print parent and child after the above code runs, I get an infinite output because to print the parent you need to print the child, and to print the child you need to print the parent... It still works for me though.
Make sure that error isn't coming about because you're trying to print the structure out in a REPL.
This could also be a limitation of Clojurescript. I can't answer for that unfortunately since I'm not familiar with Cljs.
I'll delete my answer if this ends up not being helpful.
I'll also note that nesting atoms isn't a great idea. atoms are really mutable containers to hold immutable objects. It may be cleaner to have an ID system, and have each parent/child hold a numeric ID to it's child/parent. Then you have one single atom for your whole program. The atoms can hold a map of ID->element for easy lookups.
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]])))
I am trying to get into ClojureScript and Om. There is a specific case which has me running in circles.
I have a component that is first rendered without a key.
(defn model-view [data owner]
(reify
om/IWillMount
(will-mount [_]
(om/transact! data [:stats] (fn [] {}))
(go
(let [response ((<! (api/get-stats (data :id))) :body)
stats (:stats response)]
(om/update! data [:stats] stats))))
om/IRender
(render [_]
(dom/div nil
(dom/h3 nil (data :title))
;; Here I want to use the :stats key in data that I
;; queried for in IWillMount, but its not present
;; the first time this model is rendered. It's only present
;; AFTER IWillMount has ran.
(om/build model-stats-view (data :stats)))))
The first time this component is called, the :stats key is simply not present in data. That's why I do an API call to get its stats. But React still calls the render function, thus the component crashes.
How can I set an initial state in this component that gives data an empty map called :stats, thus preventing trying to render nil in the (om/build model-stats-view) call?
I prefer to do all of my initialization in init-state, then access it in render-state. And I put a go-loop in my did-mount. When you update your init-state (i.e. :e-map) in the go-loop, it forces a call to render/re-render of the component. I use this in all of my components for inter-component/intra-component messaging. Just push something into a pub/sub channel and we are off to the races.
;To update my state I use a function:
(defn set-owner-state! [owner old-map-key old-map new-map]
(om/set-state! owner {old-map-key (merge old-map new-map)}))
om/IInitState
(init-state [_]
(println "queue->init-state")
{:e-map {:active-fsm nil}})
om/IDidMount
(did-mount [_]
(go-loop []
(let [[v _] (alts! [server-fsm-events dispatcher-events])
current-state (om/get-state owner)
e-map (:e-map current-state)]
; what goes in here is the logic to respond to a message
; in my case I have a match, it could be a cond or a set of
; if's.
(set-owner-state! owner :e-map e-map {:active-fsm :active :task-paths nil})
...
om/IRenderState
(render-state [_ {:keys [e-map]}]
(println "e-map:" e-map)
...
Is there an easier/more idiomatic way to store/swap keywords in atoms than:
(def a (atom :a))
(defn change-a [new-kw] (swap! a (fn [_] new-kw)))
No use case as yet, just wondering. It's also entirely possible that I'm missing something, and this kind of thing shouldn't happen/never actually happens in the wild because [some other pattern] is a much better solution.
atoms can be either swapped (CAS) or reset to a different value. Swapping is done with a modifying function e.g. :
user=> (swap! (atom 41) inc)
42
Notice that a previous (e.g. current) value of an atom is taken into a count when swapping.
Reseting a value of an atom is done "without regard for the current value":
user=> (reset! (atom 41) 42)
42
In your case it could be used as:
(reset! a :b)
In case you'd like to reset a value of an atom keeping the CAS semantics, you can compare-and-set! it:
user=> (def a (atom 41))
#'user/a
user=> (compare-and-set! a #a 42)
true
user=> #a
42