What's the difference between?
1.
(def x (ref 0))
(dosync
(commute x f))
2.
(def x (atom 0))
(swap! x f))
These two examples work equally
If you have only one ref and one atom, there actually is no big difference. I think you might learn something about the difference of atoms and refs.
commute is a function which can be called in any place in the dosync block. Clojure itself chooses when to execute an commute inside this block.
Maybe I explan it with a small example:
(def x (ref 0))
(def y (ref 0))
(dosync (alter x inc)
(alter y dec))
alter changes the value of a ref inside a dosync block. Because refs are coordinated and synchronous, we need to place them inside this block. If here occurs an error while altering one of the refs, the whole block fails and your refs have the same value as they had before calling the alters.
The difference to commute is the following: If we have the following code block
(def x (ref 0))
(def y (ref 0))
(dosync (commute x inc)
(alter y dec))
If an error occurs here while changing the value of your refs, commute is still called and it changes your ref. It does not care about the result of the alter and even if the alter fails, commute does increment x.
Clojure can choose when to call commute, so it can be executed even if an error occurs.
The big difference to atoms is that atoms are not coordinated. If there are multiple swap! on one atom, maybe just the first swap! will be executed. One of my projects got stuck on this issue, I had to ensure that the first swap! has terminated before the next swap! is called.
So again: Think of what you really need. Do you need an atom or some refs? If you choose refs, you have the choice between alter, which is executed at the position you want it to, or commute where Clojure can decide when to call it (the call is then commutative).
If you choose an atom, you can only change one value while this atom is executed. If another call trys to swap! the value, it is rejected.
Related
(def alice-height
(ref 3))
(def right-hand-bites
(ref 10))
(defn eat-from-right-hand []
(dosync
(when (pos? #right-hand-bites)
(alter right-hand-bites dec)
(alter alice-height #(+ % 24)))))
This code is from the book Living Clojure. In the book, the author also gave an example with alter replaced by commute. I'm wondering with the pos? test at the beginning, can we really do this replacement?
No, replacing alter with commute when decrementing right-hand-bites is not correct.
The intention of the conditional is apparently to prevent right-hand-bites from becoming negative. The decrement is only valid under the assumption that right-hand-bites won’t change until the end of the transaction. While, like alter, commute has its own snapshot view of the ref world, it will re-read and re-apply the commute function to the ref at commit time, and that would be a mistake in this program.
So, with commute it is possible to commit a negative value to right-hand-bites.
Either stick with alter, or use ensure instead of # (though that makes the whole commute exercise rather pointless).
Suppose I have the following piece of (contrived) Clojure code:
(def c (clojure.lang.Atom. [nil nil]))
(swap! c
(fn [[x y]]
["done", (second (swap! c (fn [[x y]] [x y])))]))
I would expect this to work as follows:
Clojure unboxes c to find [nil nil], and passes it to the outer fn.
The outer fn calls swap!, which unboxes c to find [nil nil], and passes this value to the inner fn.
The inner fn returns [nil nil]. The inner call to swap! swaps this in for the new value of c.
The outer fn returns the value ["done" nil].
The outer swap! tries to compare-and-set!, and sees that the current value of c, [nil nil], is the same as the old value, [nil nil], so it succeeds, swapping in ["done" nil].
But in fact, this code loops forever: the outer loop keeps retrying.
Why is this? What's missing from my mental model?
A couple people on the Clojure Slack helped me understand what was going on here (thanks!). compare-and-set! uses identical? (i.e., Java reference equality) to compare the old and new values of an atom. Every time I recreate the vector [x y], I am creating a new object, which is not identical? to the old one. Even though I don't think of the inner swap! as mutating anything, technically it is.
What is the simplest way to trigger a side-effecting function to be called only when an atom's value changes?
If I were using a ref, I think I could just do this:
(defn transform-item [x] ...)
(defn do-side-effect-on-change [] nil)
(def my-ref (ref ...))
(when (dosync (let [old-value #my-ref
_ (alter! my-ref transform-item)
new-value #my-ref]
(not= old-value new-value)))
(do-side-effect-on-change))
But this seems seems a bit roundabout, since I'm using a ref even though I am not trying to coordinate changes across multiple refs. Essentially I am using it just to conveniently access the old and new value within a successful transaction.
I feel like I should be able to use an atom instead. Is there a solution simpler than this?
(def my-atom (atom ...))
(let [watch-key ::side-effect-watch
watch-fn (fn [_ _ old-value new-value]
(when (not= old-value new-value)
(do-side-effect-on-change)))]
(add-watch my-atom watch-key watch-fn)
(swap! my-atom transform-item)
(remove-watch watch-key))
This also seems roundabout, because I am adding and removing the watch around every call to swap!. But I need this, because I don't want a watch hanging around that causes the side-effecting function to be triggered when other code modifies the atom.
It is important that the side-effecting function be called exactly once per mutation to the atom, and only when the transform function transform-item actually returns a new value. Sometimes it will return the old value, yielding new change.
(when (not= #a (swap! a transform))
(do-side-effect))
But you should be very clear about what concurrency semantics you need. For example another thread may modify the atom between reading it and swapping it:
a = 1
Thread 1 reads a as 1
Thread 2 modifies a to 2
Thread 1 swaps a from 2 to 2
Thread 1 determines 1 != 2 and calls do-side-effect
It is not clear to me from the question whether this is desirable or not desirable. If you do not want this behavior, then an atom just will not do the job unless you introduce concurrency control with a lock.
Seeing as you started with a ref and asked about an atom, I think you have probably given some thought to concurrency already. It seems like from your description the ref approach is better:
(when (dosync (not= #r (alter r transform))
(do-side-effect))
Is there a reason you don't like your ref solution?
If the answer is "because I don't have concurrency" Then I would encourage you to use a ref anyway. There isn't really a downside to it, and it makes your semantics explicit. IMO programs tend to grow and to a point where concurrency exists, and Clojure is really great at being explicit about what should happen when it exists. (For example oh I'm just calculating stuff, oh I'm just exposing this stuff as a web service now, oh now I'm concurrent).
In any case, bear in mind that functions like alter and swap! return the value, so you can make use of this for concise expressions.
I'm running into the same situation and just come up 2 solutions.
state field :changed?
Keeping a meanless :changed mark in atom to track swap function. And take the return value of swap! to see if things changed. For example:
(defn data (atom {:value 0 :changed? false}))
(let [{changed? :changed?} (swap! data (fn [data] (if (change?)
{:value 1 :changed? true}
{:value 0 :change? false})))]
(when changed? (do-your-task)))
exception based
You can throw an Exception in swap function, and catch it outside:
(try
(swap! data (fn [d] (if (changed?) d2 (ex-info "unchanged" {})))
(do-your-task)
(catch Exception _
))
Is it possible to create a ref with a transducer in Clojure, in a way analogous to creating a chan with a transducer?
i.e., when you create a chan with a transducer, it filters/maps all the inputs into the outputs.
I'd expect there's also a way to create a ref such that whatever you set, it can either ignore or modify the input. Is this possible to do?
Adding a transducer to a channel modifies the contents as they pass through, which is roughly analogous to adding a watch to a ref that applies it's own change each time the value changes. This change it's self then triggers the watch again so be careful not to blow the stack if they are recursive.
user> (def r (ref 0))
#'user/r
user> (add-watch r :label
(fn [label the-ref old-state new-state]
(println "adding that little something extra")
(if (< old-state 10) (dosync (commute the-ref inc)))))
#<Ref#1af618c2: 0>
user> (dosync (alter r inc))
adding that little something extra
adding that little something extra
adding that little something extra
adding that little something extra
adding that little something extra
adding that little something extra
adding that little something extra
adding that little something extra
adding that little something extra
adding that little something extra
adding that little something extra
1
user> #r
11
You could even apply a transducer to the state of the atom if you wanted.
This is an interesting idea, but the wrong way to go about it for at least a couple reasons. You'd lose some relationships you'd expect to hold:
(alter r identity) =/= r
(alter r f)(alter r f) =/= (alter r (comp f f))
(alter r f) =/= (ref-set r (f #r))
Also some transducers are side-effecting volatiles, and have no business in a dosync block. i.e. if you use (take n) as your transducer then if your dosync fails, then it'll retry as though invoked with (take (dec n)), which violates dosync body requirements.
The problem is a ref lets you read and write as separate steps. If instead there was something foundational that let you "apply" an input to a hidden "state" and collect the output all in one step, consistently with the STM, then that'd be something to work with.
Is there a more idiomatic/readable way of writing a get-and-set function in Clojure than:
(def the-ref (ref {}))
(defn get-and-set [new-value]
(dosync
(let [old-value #the-ref]
(do
(ref-set the-ref new-value)
old-value))))
for the simple cases I tend to see this operation used directly instead of wrapped in a function:
hello.core> (dosync (some-work #the-ref) (ref-set the-ref 5))
5
In this case dosync generally serves as the wrapper you are looking for. within the dosync This is significant because dosync composes nicely with other transactions and makes the bounds of the transaction visible. If you are in a position where the wrapper function can completely encapsulate all the references to the ref then perhaps refs are not the best tool.
A typical use of refs could look more along these lines:
(dosync (some-work #the-ref #the-other-ref) (ref-set the-ref #the-other-ref))
The need to wrap it is rare because when ref's are used they typically are use in groups of more than one ref because coordinated changes are required by the problem at hand. In cases where their is just one value then atoms are more common.