alter vs. commute

342 views
Skip to first unread message

stuart@gmail

unread,
Aug 18, 2008, 5:32:48 PM8/18/08
to clo...@googlegroups.com
Hi all,

kotarak, Chouser and I were discussing alter vs. commute over on the
IRC channel (http://clojure-log.n01se.net/date/2008-08-18.html) and I
wanted to see if our understanding is correct. Consider a simple
counter:

(defn make-counter []
(let [val (ref -1)]
(fn [] (dosync (alter val inc)))))

Theory: In a function like this, you *must* use alter, not commute.
The final state of the counter is certainly commutative, but the
purpose of the counter is to dole out each nonnegative integer exactly
once, which commute might not do.

This is based on the following assumptions about dosync:

(1) dosync returns the value of its last expression
(2) if the last expression is itself transactional, it returns the
value of the ref at commit.

True or false?

Stuart

Stephen C. Gilardi

unread,
Aug 18, 2008, 6:09:46 PM8/18/08
to clo...@googlegroups.com

On Aug 18, 2008, at 5:32 PM, stuart@gmail wrote:

> Hi all,
>
> kotarak, Chouser and I were discussing alter vs. commute over on the
> IRC channel (http://clojure-log.n01se.net/date/2008-08-18.html) and I
> wanted to see if our understanding is correct. Consider a simple
> counter:
>
> (defn make-counter []
> (let [val (ref -1)]
> (fn [] (dosync (alter val inc)))))
>
> Theory: In a function like this, you *must* use alter, not commute.
> The final state of the counter is certainly commutative, but the
> purpose of the counter is to dole out each nonnegative integer exactly
> once, which commute might not do.
>
> This is based on the following assumptions about dosync:
>
> (1) dosync returns the value of its last expression

It does.

> (2) if the last expression is itself transactional, it returns the
> value of the ref at commit.

That's right.

I think the conclusion is false though and commute will work and alter
won't.

With commute, at the end of the transaction, inc will be applied to
the the most recently committed value. With alter, the value at the
end of the transaction will be one more than the value at the
beginning of the transaction. The value at the beginning of the
transaction can easily be the same for several different threads.

--Steve

Rich Hickey

unread,
Aug 18, 2008, 7:45:56 PM8/18/08
to clo...@googlegroups.com

On Aug 18, 2008, at 6:09 PM, Stephen C. Gilardi wrote:

>
>
> On Aug 18, 2008, at 5:32 PM, stuart@gmail wrote:
>
>> Hi all,
>>
>> kotarak, Chouser and I were discussing alter vs. commute over on the
>> IRC channel (http://clojure-log.n01se.net/date/2008-08-18.html) and I
>> wanted to see if our understanding is correct. Consider a simple
>> counter:
>>
>> (defn make-counter []
>> (let [val (ref -1)]
>> (fn [] (dosync (alter val inc)))))
>>
>> Theory: In a function like this, you *must* use alter, not commute.
>> The final state of the counter is certainly commutative, but the
>> purpose of the counter is to dole out each nonnegative integer
>> exactly
>> once, which commute might not do.
>>
>> This is based on the following assumptions about dosync:
>>
>> (1) dosync returns the value of its last expression
>
> It does.
>
>> (2) if the last expression is itself transactional, it returns the
>> value of the ref at commit.
>

See # 1.

In particular, see (doc alter) and (doc commute), which indicate what
they return.

> I think the conclusion is false though and commute will work and alter
> won't.

Given #1 above, and the fact that commute will return the in-
transaction value, which is not necessarily going to be the end-of-
transaction value, the absolute value should not be depended upon,
either in calculations within the transaction or as the return value
of the transaction, as happens above.

When could it be useful to use the return value of commute? Only
indirectly/relatively, as in, a commute adding a key will yield a set/
map containing that key, but not necessarily the same map at the end-
of-transaction. Similarly, incrementing a non-negative counter will
ensure a non-zero counter, etc.

But in the case above, if you really want to return the absolute
value, use alter.

>
> With commute, at the end of the transaction, inc will be applied to
> the the most recently committed value. With alter, the value at the
> end of the transaction will be one more than the value at the
> beginning of the transaction. The value at the beginning of the
> transaction can easily be the same for several different threads.
>

But only one will succeed, so they will all be unique.

Rich

Shawn Hoover

unread,
Aug 18, 2008, 8:35:35 PM8/18/08
to clo...@googlegroups.com
On Mon, Aug 18, 2008 at 2:32 PM, stuart@gmail <stuart....@gmail.com> wrote:

Hi all,

kotarak, Chouser and I were discussing alter vs. commute over on the
IRC channel (http://clojure-log.n01se.net/date/2008-08-18.html) and I
wanted to see if our understanding is correct. Consider a simple
counter:

(defn make-counter []
  (let [val (ref -1)]
    (fn [] (dosync (alter val inc)))))

Theory: In a function like this, you *must* use alter, not commute.
The final state of the counter is certainly commutative, but the
purpose of the counter is to dole out each nonnegative integer exactly
once, which commute might not do.

Here's some code based on the web site's Refs sample that runs a transactional counter n times in a thread pool, catching the counted values in an agent just for fun: http://paste.lisp.org/display/65512.

At the end it makes sure all the numbers were counted with no duplicates. As expected I can't break it with alter, but commute breaks it regularly. (n=1 million also breaks it on my machine, probably because I have all the workers and invokeAll's futures in memory at the same time.)

Shawn

Stephen C. Gilardi

unread,
Aug 18, 2008, 9:39:10 PM8/18/08
to clo...@googlegroups.com

On Aug 18, 2008, at 7:45 PM, Rich Hickey wrote:

>>> (2) if the last expression is itself transactional, it returns the
>>> value of the ref at commit.
>>
>
> See # 1.
>
> In particular, see (doc alter) and (doc commute), which indicate what
> they return.

I looked at http://clojure.org/refs under "alter". That could use an
update to agree with "(doc alter)". Also, in (doc alter), there are
two uses of the phrase "in-transaction-value of ref" that refer to
different values. It might be helpful to explicitly say (something
like) "the new in-transaction-value of ref" for the second one (or
anything else that makes it crystal clear what happens).

>> I think the conclusion is false though and commute will work and alt

>> won't.
>
> Given #1 above, and the fact that commute will return the in-
> transaction value, which is not necessarily going to be the end-of-
> transaction value, the absolute value should not be depended upon,
> either in calculations within the transaction or as the return value
> of the transaction, as happens above.

I see now, thanks. In my uses of commute, I've returned the value of
the ref after the do-sync is complete rather than the value returned
by commute. Is it true that that would work regardless of whether one
used alter or commute?

> But in the case above, if you really want to return the absolute
> value, use alter.

Is it correct that the Clojure ensures that only one thread at a time
can proceed all the way from an alter call for a given ref and the
commit call where it is committed?

>> With commute, at the end of the transaction, inc will be applied to
>> the the most recently committed value. With alter, the value at the
>> end of the transaction will be one more than the value at the
>> beginning of the transaction. The value at the beginning of the
>> transaction can easily be the same for several different threads.
>
> But only one will succeed, so they will all be unique.

I see, thanks.

--Steve

Rich Hickey

unread,
Aug 19, 2008, 5:02:38 PM8/19/08
to clo...@googlegroups.com

On Aug 18, 2008, at 9:39 PM, Stephen C. Gilardi wrote:

>
>
> On Aug 18, 2008, at 7:45 PM, Rich Hickey wrote:
>
>>>> (2) if the last expression is itself transactional, it returns the
>>>> value of the ref at commit.
>>>
>>
>> See # 1.
>>
>> In particular, see (doc alter) and (doc commute), which indicate what
>> they return.
>
> I looked at http://clojure.org/refs under "alter". That could use an
> update to agree with "(doc alter)". Also, in (doc alter), there are
> two uses of the phrase "in-transaction-value of ref" that refer to
> different values.

No they don't. In what way do they seem different?

> It might be helpful to explicitly say (something
> like) "the new in-transaction-value of ref" for the second one (or
> anything else that makes it crystal clear what happens).
>
>>> I think the conclusion is false though and commute will work and alt
>>> won't.
>>
>> Given #1 above, and the fact that commute will return the in-
>> transaction value, which is not necessarily going to be the end-of-
>> transaction value, the absolute value should not be depended upon,
>> either in calculations within the transaction or as the return value
>> of the transaction, as happens above.
>
> I see now, thanks. In my uses of commute, I've returned the value of
> the ref after the do-sync is complete rather than the value returned
> by commute. Is it true that that would work regardless of whether one
> used alter or commute?
>

Yes, after the dosync you are referring to the ref's final value, the
transaction is over.

Rich

Stephen C. Gilardi

unread,
Aug 21, 2008, 7:25:38 PM8/21/08
to clo...@googlegroups.com
On Aug 19, 2008, at 5:02 PM, Rich Hickey wrote:
> On Aug 18, 2008, at 9:39 PM, Stephen C. Gilardi wrote:
>> I looked at http://clojure.org/refs under "alter". That could use an
>> update to agree with "(doc alter)". Also, in (doc alter), there are
>> two uses of the phrase "in-transaction-value of ref" that refer to
>> different values.
>
> No they don't. In what way do they seem different?

The "in-transaction value of ref" is one value before the call to
alter and another value after the call. The "before" and "after"
values are the two values I'm talking about.

Here's the doc for alter:

user=> (doc alter)
-------------------------
clojure/alter
([ref fun & args])
Must be called in a transaction. Sets the in-transaction-value of
ref to:

(apply fun in-transaction-value-of-ref args)

and returns the in-transaction-value of ref.

While the doc for alter is correct, I was suggesting that the fact
that clojure/alter returns the "after" value be made unmistakable.
Something like:

user=> (doc alter)
[...]

and returns the updated in-transaction-value of ref.

The word "updated" makes it crystal clear which "in-transaction value"
of ref ("before" or "after") is returned.

--Steve

Reply all
Reply to author
Forward
0 new messages