Skip to content

Instantly share code, notes, and snippets.

@NicMcPhee
Last active September 16, 2019 10:59
Show Gist options
  • Save NicMcPhee/4957518 to your computer and use it in GitHub Desktop.
Save NicMcPhee/4957518 to your computer and use it in GitHub Desktop.
Three different ways of doing counters in Clojure, one of which doesn't work and thereby illustrates the scope of atomicity and a difference between atom and ref.
;;;;;;;;;;;;;;;;;;
;; The first way
;;;;;;;;;;;;;;;;;;
;; This use of atom is simple and works fine.
(def counter (atom -1))
(defn next-value []
(swap! counter inc))
;;;;;;;;;;;;;;;;;;
;; The second way
;;;;;;;;;;;;;;;;;;
;; That said, it irked me slightly to have to start
;; the counter at -1, but that's necessary since swap!
;; returns the value *after* the update (and I want the
;; first value to be 0.
;; So I thought maybe I'd capture the original value
;; and then return it (as below). That does *not* work
;; however because we capture the value of @atom-counter
;; before knowing if the swap! will work or not. If the
;; swap! is executed several times we'll still have and
;; return the same initial value that we read at the start.
;; That causes the same value to be returned from
;; (atom-next-value) if we run it in several threads at the
;; same time.
;; => (pmap (fn [_] (atom-next-value)) (range 20))
;; (0 0 4 2 3 7 5 6 8 9 13 10 11 12 17 14 18 15 19 16)
(def atom-counter (atom 0))
(defn atom-next-value []
(let [result @atom-counter]
(swap! atom-counter inc)
result))
;;;;;;;;;;;;;;;;;;
;; The second way
;;;;;;;;;;;;;;;;;;
;; That problem could be fixed if we could ensure
;; that essentially the entire body of atom-next-value
;; was atomic. There's no good way to do that with
;; atoms, though, because all we've got is swap!, and
;; we can't ask swap to return the old value instead
;; of the new one.
;; We can, however, us a ref and wrap the whole thing
;; in a dosync:
(def ref-counter (ref 0))
(defn ref-next-value []
(dosync
(let [result @ref-counter]
(alter ref-counter inc)
result)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment