When I tried to learn memoize example in https://clojure.org/reference/atoms , I became confused why mem variable holds the same atom object across multiple function calls.
According to the QA in Clojure closure , this is a behavior of closure. However as the following two functions behave differntly, I come to think that this is a behavior of atom. Could you explain the difference of the following two functions from the view of closure and atom?
(def counter
(let [count (atom 0)]
(fn [] (do (swap! count inc)
@count))))
(println (counter)) ;; 1
(println (counter)) ;; 2
(def counter2
(let [count {:value 0}]
(fn [] (do (assoc count :value (inc (get count :value)))
(get count :value)))))
(println (counter2)) ;; 0
(println (counter2)) ;; 0
(EDIT) After reading Eugene's answer I replace the second example function with the following function. The reason why the counter increases its value is that the counter is a closure being closed over a variable with mutable value.
(def counter2
(let [count (int-array '(0))]
(fn [] (do (aset count 0 (inc (get count 0)))
(get count 0))))) ;; This line is not necessary but is added to keep consistency with the functions above.
(println (counter2)) ;; 1
(println (counter2)) ;; 2
Both functions are closed over a single value. The first function is closed over a container whose value can be changed. The second function is closed over an immutable map that cannot be changed.
swap!
changes the value inside an atom, in-place. (BTW don't mix atom operations like you did with swap!
followed by @
unless you're absolutely certain it must be done since it makes the code not atomic. In this case, just return the result of swap!
.)
assoc
create a new immutable map and returns not, without affecting its arguments.