Alternative version of the exchange! also returning changed cost

I talked about this a bit on the IRC # clojure channel today, but I would like to dwell more on this. Basically, to better understand atoms, swap! , deref and Clojure concurrency in general, I would like to try to write a function that not only returns the value that was replaced using swap! , but also the value that has been replaced.

 (def foo (atom 42)) . . . ((fn [a] (do (println "swapped out: " @a) (println "swapped in: "(swap! a rand-int)))) foo) 

can print:

 swapped out: 42 swapped in: 14 

However, if another thread does swap! the same atom between @a deref and the swap! call swap! , then I can replace the value, which is not equal to 42.

How can I write a function that correctly returns both values ​​(swapped and swapped)?

I am not interested in the various meanings that the atom has changed: all I want to know is what was unloaded with the value.

Could this be written using code that is guaranteed to not come to a standstill, and if so, why?

+3
multithreading concurrency atomic clojure
source share
5 answers

Clojure swap! is simply a spinning comparison and a multitude. You can define an alternative version that returns whatever you like:

 (defn alternate-swap [atom f & args] (loop [] (let [old @atom new (apply f old args)] (if (compare-and-set! atom old new) [old new] ; return value (recur))))) 
+12
source share

The atoms are not coordinated, so it seems likely that any attempt to do this outside the swap function, which it itself will most likely fail. You can write a function that you call instead of exchanging! which creates a function that saves the existing value before applying the real function, and then passes that configured function to swap! .

 user> (def foo (atom [])) #'user/foo user> (defn save-n-swap! [af & args] (swap! a (fn [old-val] (let [new-val (apply f (cons old-val args))] (println "swapped out: " old-val "\n" "swapped in: " new-val) new-val)))) #'user/save-n-swap! user> (save-n-swap! foo conj 4) swapped out: [] swapped in: [4] [4] user> (save-n-swap! foo conj 4) swapped out: [4] swapped in: [4 4] [4 4] 

This example prints it. It would also be advisable to push them into a change log stored in another atom.

+2
source share

If you want a return value, Stuart's answer is correct, but if you're just going to do a bunch of println to understand how atoms / refs work, I would recommend adding a clock to Atom / ref http://clojuredocs.org/clojure_core/1.2. 0 / clojure.core / add-watch

 (add-watch your-atom :debug (fn [_ _ old new] (println "out" old "new" new))) 
+1
source share

You can use a macro, for example:

 (defmacro swap!-> [atom & args] `(let [old-val# (atom nil) new-val# (swap! ~atom #(do (swap! old-val# (constantly %)) (-> % ~args)))] {:old @old-val# :new new-val#})) (def data (atom {})) (swap!-> data assoc :a 3001) => {:new {:a 3001} :old {}} 
+1
source share

You can rely on a promise to keep the current value inside the swap! operation swap! . Then you return the new and old value to the vector:

 (defn- swap-and-return-old-value! [^clojure.lang.IAtom atom f & args] (let [old-value-promise (promise) new-value (swap! atom (fn [old-value] (deliver old-value-promise old-value) (apply f old-value args)))] [new-value @old-value-promise])) 
0
source share

All Articles