agam
agam

Reputation: 5364

Alter vs Commute in Clojure: what am I doing wrong?

Clojure newbie here, I was going through the excellent "Clojure from the ground up" posts, and tried out the last exercise in this post.

When I replace alter with commute, the sum is inaccurate, but I don't understand why.

(def work (ref (apply list (range 1e5))))
(def sum (ref 0))

(defn trans-alter [work sum]
  (dosync
   (if-let [n (first @work)]
     (do
       (alter work rest)
       (alter sum + n)
       (count @work))
     0)))

(defn trans-commute [work sum]
  (dosync
   (if-let [n (first @work)]
     (do
       (commute work rest)
       (commute sum + n)
       (count @work))
     0)))

(I've skipped the code that sets up the futures and calls them etc)

With trans-alter here I got 4999950000 for the sum (which is the correct expected value), while with trans-commute I got a different value each time, but higher than expected (e.g. 4999998211).

What am I missing here? Thanks in advance!

Upvotes: 4

Views: 252

Answers (1)

Arthur Ulfeldt
Arthur Ulfeldt

Reputation: 91554

Commute and alter essentially do the same thing, though commute is a little more lenient on the guarantee of correctness.

Alter instructs the STM to always ensure that this code ran all the way through without any of the refs it uses changing out from under it.

Commute is an instruction to help the STM decide when it needs to abort a transaction because the underlying data changed out from under it.

If everything in a transaction is commutative, then it's ok to let that transaction finish even if some data changed. In your case two transactions could both:

  1. grab the first number
  2. remove the same number from work
  3. add the same number to result
  4. then use commute to instruct the STM that this is OK, and it should just go ahead and commit the transaction anyway...
  5. get the wrong answer.

So in short,the work you are asking to preform is not actually a commutative operation. Specifically removing an item from a list is not commutative. If you change any of the commutes to an alter, then step 4 would have kicked one of them out and only allowed one of them to finish. The one that got kicked out would be re-run on the fresh data and eventually would have arrived at a correct result.

Upvotes: 5

Related Questions