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
> 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
>
>
> 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
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.
>>> (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
>
>
> 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
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