How to change atom map in clojure?

I'm new to clojure, and i need to update two values inside this atom:

(def app-state (atom {:id "1":status 0 :st 0}))

Im using the following:

(let [value (mod (+ (:st @app-state) 1) 4)]
    (swap! app-state update-in [:status] value)
    (swap! app-state update-in [:st] inc))

Im getting:

Caused by: java.lang.ClassCastException: java.lang.Long cannot be cast to clojure.lang.IFn

Upvotes: 3

Views: 2639

Answers (2)

David Kettering
David Kettering

Reputation: 31

Below is a repl session based on your problem. First I define app-state exactly as you posted. Next, I define bump-state as an anonymous function #(…) that takes one argument, the current value of app-state, denoted by the % placeholder. The let form binds the incremented value of the :st key to st. Finally, assoc returns a new immutable map by associating new values with :st and :status of the previous map.

The next several lines just call swap! to confirm that bump-state works. The last line just defines the anonymous function directly in the call to swap!. While the accepted answer works fine, I think this is a little more succinct.

user=> (def app-state (atom {:id "1":status 0 :st 0}))
#'user/app-state
user=> (def bump-state #(let [st (inc (:st %))] (assoc % :st st :status (mod st 4))))
#'user/bump-state
user=> (swap! app-state bump-state)
{:id "1", :status 1, :st 1}
user=> (swap! app-state bump-state)
{:id "1", :status 2, :st 2}
user=> (swap! app-state bump-state)
{:id "1", :status 3, :st 3}
user=> (swap! app-state bump-state)
{:id "1", :status 0, :st 4}
user=> (swap! app-state #(let [st (inc (:st %))] (assoc % :st st :status (mod st 4))))
{:id "1", :status 1, :st 5}

The whole idea of swap! is that your update function operates on the current immutable value held by the atom and returns a new immutable value to replace it with. Your update function is simply a pure function, which lets you reason about it more easily and also play with it at the repl, just like other functions.

Upvotes: 3

Lee
Lee

Reputation: 144206

The third argument to update-in takes a function but you are supplying a long (value) which is why you get an exception. You could use assoc-in instead which takes the value to associate in the map directly:

(swap! app-state assoc-in [:status] value)

However you should do the entire update to the state atomically within the function passed to swap! e.g.

(swap! app-state (fn [{:keys [st] :as state}]
                    (let [st-next (inc st)
                          value (mod st-next 4)]
                       (merge state {:st st-next
                                     :status value}))))

Upvotes: 5

Related Questions