From: Jörg W Mittag on
Hi!

According to the specification,

primary_expression[indexing_argument_list] ω= expression

is (roughly) evaluated like

o = primary_expression
*l = indexing_argument_list
v = o.[](*l)
w = expression
(v ω w).tap {|x|
l << x
o.[]=(*l)
}

In particular, this means that the assignment *always* takes place.
However, at least in some Ruby implementations (I tested MRI, YARV,
Rubinius, JRuby and IronRuby), this doesn't actually seem to be the
case, at least for some operators:

def (h = {int: 0}).[]=(k, v) p "Setting #{k} to #{v}"; super end
h[:int] += 1
# "Setting int to 1"
h[:int] += 1
# "Setting int to 2"

This calls the setter twice, as expected per the specification.

h[:key] ||= :value
# "Setting key to value"
h[:key] ||= :value

But this doesn't.

Since the purpose of the specification is to describe the currently
existing implementations, this is obviously a bug in the specification
rather than a bug in all five currently released implementations,
especially since there is a ton of code and even a common idiom that
depends on the current behavior. But what would the correct
specification look like?

Thanks in advance,
Jörg.
From: Rein Henrichs on
On 2010-06-07 08:42:08 -0700, J�rg W Mittag said:

> h[:key] ||= :value
> # "Setting key to value"
> h[:key] ||= :value
>
> But this doesn't.
>
> Since the purpose of the specification is to describe the currently
> existing implementations, this is obviously a bug in the specification
> rather than a bug in all five currently released implementations,
> especially since there is a ton of code and even a common idiom that
> depends on the current behavior. But what would the correct
> specification look like?
>
> Thanks in advance,
> J�rg.

It is commonly thought that x <operator>= y is exactly equivalent to x
= x <operator> y in all cases. What you have just demonstrated is that,
presumably for performance reasons, h[key] ||= value is in fact
equivalent to h[key] || h[key] = value, rather than h[key] = h[key] ||
value. This prevents a second lookup in the case where k[key] exists
and is not nil or false.

As to what the correct specification would look like, I am not sure,
but I do agree with you.

--
Rein Henrichs
http://puppetlabs.com
http://reinh.com

From: Rick DeNatale on
On Mon, Jun 7, 2010 at 1:05 PM, Rein Henrichs <reinh(a)reinh.com> wrote:
> It is commonly thought that x <operator>= y is exactly equivalent to x = x
> <operator> y in all cases. What you have just demonstrated is that,
> presumably for performance reasons, h[key] ||= value is in fact equivalent
> to h[key] || h[key] = value, rather than h[key] = h[key] || value. This
> prevents a second lookup in the case where k[key] exists and is not nil or
> false.
>

x ||= y is NOT equivalent to x = x || y

Instead a closer approximation is

x || x = y

http://talklikeaduck.denhaven2.com/2008/04/26/x-y-redux


--
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Github: http://github.com/rubyredrick
Twitter: @RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale

From: Rein Henrichs on
On 2010-06-07 12:18:39 -0700, Rick DeNatale said:

> On Mon, Jun 7, 2010 at 1:05 PM, Rein Henrichs <reinh(a)reinh.com> wrote:
>> It is commonly thought that x <operator>= y is exactly equivalent to x = x
>> <operator> y in all cases. What you have just demonstrated is that,
>> presumably for performance reasons, h[key] ||= value is in fact equivalent
>> to h[key] || h[key] = value, rather than h[key] = h[key] || value. This
>> prevents a second lookup in the case where k[key] exists and is not nil or
>> false.
>>
>
> x ||= y is NOT equivalent to x = x || y
>
> Instead a closer approximation is
>
> x || x = y
>
> http://talklikeaduck.denhaven2.com/2008/04/26/x-y-redux

Good explanation in that post, Rick. Thanks!

--
Rein Henrichs
http://puppetlabs.com
http://reinh.com