# Please explain nuances of ||=

**URL:** <https://www.ruby-forum.com/t/please-explain-nuances-of/136398>\
**Category:** Ruby\
**Created:** [May 1, 2008, 6:20pm UTC](https://www.ruby-forum.com/t/please-explain-nuances-of/136398 "2008-05-01T18:20:27Z")\
**Posts on this page:** 8\
**Page:** 2

<div class="post-metadata">

**Author:** ![rubyfreak](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/e9c0ed/32.png) [@rubyfreak](https://www.ruby-forum.com/u/rubyfreak)\
**Post date:** [May 2, 2008, 9:39am UTC](https://www.ruby-forum.com/t/please-explain-nuances-of/136398/21 "2008-05-02T09:39:59Z")

</div>

2008/5/2 Rick DeNatale [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid):

> In summary:
> 
> That’s the way that Ruby works. Ruby ain’t C, or C++ or Java.

Nice summary! And, if you think about it from a usability perspective  
it does make the most sense the way it is.

1. you do not want to spent CPU cycles for assigning the same object  
to a reference

2. more importantly, in the case of Hash you do not want the Hash to  
change (which it would do if ||= were implemented differently as I  
have demonstrated earlier).

I’m out.

Cheers

robert

---

<div class="post-metadata">

**Author:** ![rubyfreak](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/e9c0ed/32.png) [@rubyfreak](https://www.ruby-forum.com/u/rubyfreak)\
**Post date:** [May 2, 2008, 3:26am UTC](https://www.ruby-forum.com/t/please-explain-nuances-of/136398/22 "2008-05-02T03:26:36Z")

</div>

Someone already mentioned it, but I recently wrote an article about  
this controversy:

[http://talklikeaduck.denhaven2.com/articles/2008/04/26/x-y-redux](http://talklikeaduck.denhaven2.com/articles/2008/04/26/x-y-redux)

In writing this article, I pulled my old dusty copy of Kernighan and  
Ritchie off my shelf, since IIRc the whole

x op= y

idea originally came from C.

And of course that’s where the oft reported meme that

x op=y

was identical to

x = x op y

originated, and was propagated through several popular expositions of  
Ruby.

BUT

C, although it does have || and && as short-circuit boolean operators,  
does NOT allow ||= or &&=

When Matz added these to Ruby he, quite cleverly IMHO, defined them as  
short circuiting as well, the assignment is NOT done if the LHS  
evaluates to true (in the Ruby sense that is, neither nil nor false).

And as I also verified, since reading about ||= in David Flanigan’s  
“The Ruby P.ming Language” got me to thinking about it, &&= also  
doesn’t do an assignment if the LHS evaluates to false.

All of this falls out naturally from 1) short-circuit evaluation of ||  
and &&, and 2) the fact that a.x = y is syntactic sugar for a.x=(y),  
and 3) a[b] = c is syntactic sugar for a.[]=(b,c).

So a foolish adherence to the idea that Ruby should slavishly follow C  
where ||= and &&= are not allowable assignment operators, and there  
are no x= or []= methods to serve as targets of syntactic sugar,  
doesnt make that sense.

So:

1. In Ruby x ||= y does no assignment if x evaluates to true, and x  
&&= y does no assignment if x evaluates to true.
2. x || x = y, and x && x = y are much better first approximations of  
how x ||= y and x &&= y than x = x || y, and x = x && y, despite what  
K&R say.
3. I say first approximations because of David Black’s observation  
that x ||= y and x || x = y differ when x is undefined.

In summary:

That’s the way that Ruby works. Ruby ain’t C, or C++ or Java.

Get over it!

–  
Rick DeNatale

My blog on Ruby  
[http://talklikeaduck.denhaven2.com/](http://talklikeaduck.denhaven2.com/)

---

<div class="post-metadata">

**Author:** ![rubyfreak](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/e9c0ed/32.png) [@rubyfreak](https://www.ruby-forum.com/u/rubyfreak)\
**Post date:** [May 2, 2008, 2:35pm UTC](https://www.ruby-forum.com/t/please-explain-nuances-of/136398/23 "2008-05-02T14:35:50Z")

</div>

- Rick DeNatale [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) (03:26) schrieb:

> 1. In Ruby x ||= y does no assignment if x evaluates to true, and x  
> &&= y does no assignment if x evaluates to true.

Eh, what? You are confusing me.

&&= does an assignment only if x evaluates to true.

The question of if there is an assignment in x = x is purely  
metaphysical: there is no way to tell.

> 1. x || x = y, and x && x = y are much better first approximations of  
> how x ||= y and x &&= y than x = x || y, and x = x && y, despite what  
> K&R say.

And doesn’t explain why x springs into existence, if didn’t exist  
before.

> 1. I say first approximations because of David Black’s observation  
> that x ||= y and x || x = y differ when x is undefined.

Right, but x = x || y doesn’t.

The only thing special about x ||= y is that x is only ever evaluated  
once. But that’s special in every op=.

mfg, simon … l

---

<div class="post-metadata">

**Author:** ![rubyfreak](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/e9c0ed/32.png) [@rubyfreak](https://www.ruby-forum.com/u/rubyfreak)\
**Post date:** [May 2, 2008, 2:37pm UTC](https://www.ruby-forum.com/t/please-explain-nuances-of/136398/24 "2008-05-02T14:37:37Z")

</div>

- Robert K. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) (21:21) schrieb:

> irb(main):009:0\> h[1] = h[1] || 10  
> =\> true

Well, the difference is that h[1] gets only evaluated once.

But now I get it: It does make a difference here if there is an  
assignment or not. So much for metaphysics. ☹

So, neither x = x || y nor x || x = y explain everything about x ||= y.

&&= and ||= are different from += and co, because of their short-cut  
behavior. They are conditional assignment operators.

mfg, simon … sfti

---

<div class="post-metadata">

**Author:** ![rubyfreak](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/e9c0ed/32.png) [@rubyfreak](https://www.ruby-forum.com/u/rubyfreak)\
**Post date:** [May 2, 2008, 3:18pm UTC](https://www.ruby-forum.com/t/please-explain-nuances-of/136398/25 "2008-05-02T15:18:51Z")

</div>

On Fri, May 2, 2008 at 8:35 AM, Simon K. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> - Rick DeNatale [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) (03:26) schrieb:
> 
> > 1. In Ruby x ||= y does no assignment if x evaluates to true, and x  
> > &&= y does no assignment if x evaluates to true.
> 
> Eh, what? You are confusing me.
> 
> &&= does an assignment only if x evaluates to true.

Yes, that was a typo on my part, it should have read “and x &&= y  
does not assignment if x evaluates to false.”

> The question of if there is an assignment in x = x is purely  
> metaphysical: there is no way to tell.

If x is a simple variable, that’s correct, however if x is actually an  
“attribute accessor” like

foo.bar ||= y  
or  
foo[:bar] ||= y

Then you can tell if the bar= or []= method which gets invoked on  
‘assignment’ has side effects.

> > 1. x || x = y, and x && x = y are much better first approximations of  
> > how x ||= y and x &&= y than x = x || y, and x = x && y, despite what  
> > K&R say.
> 
> And doesn’t explain why x springs into existence, if didn’t exist  
> before.

This is because the Ruby parser recognizes the variable as local when  
it sees it as the POTENTIAL assignee. In the first chunk below, the a  
= 1 never got executed because of the if false modifier, but the  
parser still picked up a ‘declaration’ of a as a local variable.

defined? a # =\> nil  
a = 1 if false  
defined? a # =\> “local-variable”  
a # =\> nil

defined? b # =\> nil  
b ||= 1  
defined? b # =\> “local-variable”  
b # =\> 1

defined? c # =\> nil  
c &&= 1  
defined? c # =\> “local-variable”  
c # =\> nil

of course in the case of

d.e ||= g

or

h[1] ||= i

There’s no question of the method ‘springing’ into existence, d.e,  
d.e=, h[], and h[]= will either work or throw a method not found if  
and when they are called.

> > 1. I say first approximations because of David Black’s observation  
> > that x ||= y and x || x = y differ when x is undefined.
> 
> Right, but x = x || y doesn’t.
> 
> The only thing special about x ||= y is that x is only ever evaluated  
> once. But that’s special in every op=.

Except that, if x is really obj.foo, or obj[a] then the notion of x  
getting evaluated once is a little squirrelly, because reading x is  
done by evaluating obj.foo or obj., and writing it (should the  
assignment actually occur) is done by evaluating obj.foo=(y) or  
obj.[]=(a,y)

The fact that x = y is actually a method call under some circumstances  
is what’s special about Ruby, and why simply extending K&Rs  
explanation of op= misses the point.

–  
Rick DeNatale

My blog on Ruby  
[http://talklikeaduck.denhaven2.com/](http://talklikeaduck.denhaven2.com/)

---

<div class="post-metadata">

**Author:** ![rubyfreak](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/e9c0ed/32.png) [@rubyfreak](https://www.ruby-forum.com/u/rubyfreak)\
**Post date:** [May 2, 2008, 6:12pm UTC](https://www.ruby-forum.com/t/please-explain-nuances-of/136398/26 "2008-05-02T18:12:47Z")

</div>

2008/5/2 Rick DeNatale [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid):

> 1. I say first approximations because of David Black’s observation  
> that x ||= y and x || x = y differ when x is undefined.

They obviously also differ if evaluating x has side effects as in

h = {}  
h[print(“f”)] ||= 1

h = {}  
h[print(“o”)] || h[print(“o”)] = 1

# =\> foo

so x ||= y is more like

ref = &x; \*ref || \*ref = y

with the reference (&) and dereference (\*) operators of C.

Regards,  
Pit

---

<div class="post-metadata">

**Author:** ![rubyfreak](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/e9c0ed/32.png) [@rubyfreak](https://www.ruby-forum.com/u/rubyfreak)\
**Post date:** [May 2, 2008, 10:05pm UTC](https://www.ruby-forum.com/t/please-explain-nuances-of/136398/27 "2008-05-02T22:05:38Z")

</div>

- Rick DeNatale [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) (20:05) schrieb:

> Ruby 1.9 makes it somewhat easier to see what’s really happening.

Thanks a lot!

> k$ ruby1.9 -ve’puts VM::InstructionSequence.compile(“h={};h["a" +  
> "b"] ||= 3”).disasm’

Can you give any documentation pointers for this cool stuff?

mfg, simon … l

---

<div class="post-metadata">

**Author:** ![rubyfreak](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/e9c0ed/32.png) [@rubyfreak](https://www.ruby-forum.com/u/rubyfreak)\
**Post date:** [May 2, 2008, 8:06pm UTC](https://www.ruby-forum.com/t/please-explain-nuances-of/136398/28 "2008-05-02T20:06:27Z")

</div>

On Fri, May 2, 2008 at 12:12 PM, Pit C. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
wrote:

> h = {}  
> h[print(“o”)] || h[print(“o”)] = 1
> 
> # =\> foo
> 
> so x ||= y is more like
> 
> ref = &x; \*ref || \*ref = y
> 
> with the reference (&) and dereference (\*) operators of C.

But x is different whether its on the RHS or LHS

The problem is that it’s not a textual substitution.

h[1+2] ||= 3 is like

arg\_temp = 1 + 2  
h[arg\_temp] || h[arg\_temp] = 3

Keep in mind that unlike operators like + and -, || and && are really  
control flow ‘operators’ implemented by testing and branching. The  
same is true of ||= and &&=, which I suppose is the point.

Ruby 1.9 makes it somewhat easier to see what’s really happening.

k$ ruby1.9 -ve’puts VM::InstructionSequence.compile(“h={};h["a" +  
"b"] ||= 3”).disasm’  
ruby 1.9.0 (2008-03-21 revision 0) [i686-darwin9.2.2]  
== disasm: \<ISeq:@\>=================================  
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1]  
s1)  
[2] h  
0000 newhash 0 (  
1)  
0002 setlocal h  
0004 getlocal h  
0006 putstring “a”  
0008 putstring “b”  
0010 opt\_plus  
0011 dupn 2  
0013 opt\_aref  
0014 dup  
0015 branchif 28  
0017 pop  
0018 putobject 3  
0020 send :[]=, 2, nil, 0,   
0026 leave  
0027 pop  
0028 swap  
0029 pop  
0030 swap  
0031 pop  
0032 leave

I’m sure that Ruby 1.8 does pretty much the same thing, but I don’t  
have the time to dig through parse.y and eval.c to prove it.

–  
Rick DeNatale

My blog on Ruby  
[http://talklikeaduck.denhaven2.com/](http://talklikeaduck.denhaven2.com/)

[Previous page](https://www.ruby-forum.com/t/please-explain-nuances-of/136398.md?page=1)
