Question about abbreviated indexing assignments

Hi!

According to the specification,

primary_expression[indexing_argument_list] ω= expression

is (roughly) evaluated like

o = primary_expression
*l = indexing_argument_list
v = o.
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.

On 2010-06-07 08:42:08 -0700, Jörg W Mittag said:

depends on the current behavior. But what would the correct
specification look like?

Thanks in advance,
Jörg.

It is commonly thought that x = y is exactly equivalent to x
= x 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.

On Mon, Jun 7, 2010 at 1:05 PM, Rein H. [email protected] wrote:

It is commonly thought that x = y is exactly equivalent to x = x
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: rubyredrick (Rick DeNatale) · GitHub
Twitter: @RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale

On 2010-06-07 12:18:39 -0700, Rick DeNatale said:

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!