Hi there, I have some state which I'd like to set to some default value, A. I'd then like to update A to a new value A' and then, if (not (= A A')) I'd like to fire off a function - say print to stdout that A has changed. If (= A A') I'd like nothing to happen at all. Additionally, I'd like to do this in a way that's thread safe - i.e. I can be updating A from any given thread and I'd never like to miss a time when A has changed and also I don't want to fire of the fn more times than necessary.
An initial naive solution might be the following: (def a (atom 1)) (defn changed-fn [] (println "something changed!")) (defn new-val [] (int (rand 2))) (defn update [] (let [old-a @a ;;A new-a (reset! a (new-val)] ;;B (when-not (= old-a new-a) (changed-fn)))) However, if there are two updates running concurrently, statements A and B may be interleaved such that if we have two threads X and Y, a starting val of 0 for a and the result of calling new-val be 0 each time: X (deref a) ;=> 1 Y (deref a) ;=> 1 X (reset! a 0) ;=> 0 Y (reset! a 0) ;=> 0 We now call our changed-fn twice. If however, we don't interleave the calls: X (deref a) ;=> 1 X (reset! a 0) ;=> 0 Y (deref a) ;=> 0 Y (reset! a 0) ;=> 0 this results in changed-fn called only once. Clearly this approach doesn't work. Ideally, it seems that there could be versions of reset! and swap! that returned a vector result containing the old and new vals which could be directly compared. The solution appears to be to use a transaction and bind the result of a comparison within the dosync: (def a (ref 1)) (defn update [] (let [changed? (dosync (let [old-a @a new-a (ref-set a (new-val))] (= old-a new-a)))] (when changed? (changed-fn)))) Is this the only way of achieving this? Is there another, perhaps more idiomatic, approach? It certainly seems overkill to have to use a transaction when the number of references we wish to coordinate is only one - itself and its previous value. Sam --- http://sam.aaron.name -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en