Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

The ||= assignment operator

2,328 views
Skip to first unread message

Belorion

unread,
Apr 4, 2008, 3:11:31 PM4/4/08
to
[Note: parts of this message were removed to make it a legal post.]

It was my understanding that the ||= assignment operator assigned the value
on the right-hand side if and only if the left hand side did not already
have a value:

irb(main):001:0> x = true
=> true
irb(main):002:0> x ||= "ruby"
=> true
irb(main):003:0> x
=> true
And, likewise, with nil:

irb(main):014:0> x = nil
=> nil
irb(main):015:0> x ||= "ruby"
=> "ruby"
irb(main):016:0> x
=> "ruby"
However, I do not understand this behavior:

irb(main):019:0> x = false
=> false
irb(main):020:0> x ||= "ruby"
=> "ruby"
irb(main):021:0> x
=> "ruby"


We know that false != nil, and yet the ||= will assign if the left hand side
is false?

regards,
Matt

Glen Holcomb

unread,
Apr 4, 2008, 3:25:04 PM4/4/08
to
[Note: parts of this message were removed to make it a legal post.]

Nope ||= will assign if nil or false.

--
"Hey brother Christian with your high and mighty errand, Your actions speak
so loud, I can't hear a word you're saying."

-Greg Graffin (Bad Religion)

Todd Benson

unread,
Apr 4, 2008, 3:28:24 PM4/4/08
to

The operators are working with a three-valued logic. In a comparison,
a FalseClass object or a NilClass object will be logistically false.

Todd

Paul Mucur

unread,
Apr 4, 2008, 3:30:29 PM4/4/08
to
On 4 Apr 2008, at 20:11, Belorion wrote:

> It was my understanding that the ||= assignment operator assigned
> the value
> on the right-hand side if and only if the left hand side did not
> already
> have a value:

It's probably better to think of x ||= "ruby" as being short hand for
x = x || "ruby" (which it effectively is) instead of "assign if not
already having a value". If you do this, then you can see why your
last example behaves like it does.

Don't forget that there are other similar operators like +=, -= and
&&= and they all follow the same pattern:

x ?= y
x = x ? y

Where ? is one of -, +, && or ||.

Hope that this helps.

Belorion

unread,
Apr 4, 2008, 3:41:38 PM4/4/08
to
[Note: parts of this message were removed to make it a legal post.]

Thanks all, that clears up my understanding. I hadn't thought of it as x =
x || "ruby" (even thoughI knew that x += 1 is the same as x = x + 1) and
that in that expanded case, x evaluates, logistically, to false if nil or
false.

Matt

Todd Benson

unread,
Apr 4, 2008, 3:51:54 PM4/4/08
to
On Fri, Apr 4, 2008 at 2:41 PM, Belorion <belo...@gmail.com> wrote:
> Thanks all, that clears up my understanding. I hadn't thought of it as x =
> x || "ruby" (even thoughI knew that x += 1 is the same as x = x + 1) and
> that in that expanded case, x evaluates, logistically, to false if nil or
> false.

For conditional expressions. Here's something that may catch you off guard...

irb(main):001:0> nil || false
=> false
irb(main):001:0> false || nil
=> nil

Todd

Chris Shea

unread,
Apr 4, 2008, 4:14:33 PM4/4/08
to

It's better to think of x ||= "ruby" as x || x = "ruby".

--
h = Hash.new('default value')

h[:test] ||= 'assigned value'
h # => {}

# same as:
h[:test] || h[:test] = 'assigned value'
h # => {}

# not:
h[:test] = h[:test] || 'assigned value'
h # => {:test=>"default value"}
--

Chris

Peña, Botp

unread,
Apr 4, 2008, 11:29:04 PM4/4/08
to
From: Chris Shea [mailto:cms...@gmail.com]
# It's better to think of x ||= "ruby" as x || x = "ruby".
#
# --
# h = Hash.new('default value')
#
# h[:test] ||= 'assigned value'
# h # => {}
#
# # same as:
# h[:test] || h[:test] = 'assigned value'
# h # => {}
#
# # not:
# h[:test] = h[:test] || 'assigned value'
# h # => {:test=>"default value"}


i'd say that's a bug in design (unless x+=1 is now x+x=1 ;)

kind regards -botp


Avdi Grimm

unread,
Apr 4, 2008, 11:53:17 PM4/4/08
to
On this topic, am I the only one who would really like to see a
dedicated "defaulting operator" in Ruby? Something that actually has
the semantics which a lot of people mistakenly assume for ||=, i.e.
"assign if and only if nil"? I think Perl6 uses the //= operator for
this. This is such a subtle gotcha. I've seen a lot of code that was
potentially buggy because it didn't account for the fact that the
'false' value would be handled incorrectly.

--
Avdi

s.ross

unread,
Apr 5, 2008, 12:38:42 AM4/5/08
to

This would be great, but you would need both a // and //= operator.
Consider this:

@my_web_page_title = @page_name || "d'oh! you forgot to name your page"

You would want to rewrite this as:

@my_web_page_title = @page_name // "d'oh! you forgot to name your page"

It would seem that adding these operators has a low potential to
introduce bugs into working code and, as you point out, there are
subtleties WRT the ||= idiom. Moving it from idiom to first-class
operator might clarify those subtleties.


Joshua Ballanco

unread,
Apr 5, 2008, 1:46:11 AM4/5/08
to

Not at all. Rather, this is just a subtle misunderstanding of how hash
is implemented. Consider the following:

>> h = Hash.new
=> {}
>> h[:test] ||= 'testing without default'
=> "testing without default"
>> h
=> {:test=>"testing without default"}


>> h = Hash.new('default value')

=> {}
>> h[:test] ||= 'testing with default'
=> "default value"
>> h
=> {}

The only reason that Chris' example behaves like "x || x = stuff" is
because he's defined a default value for the hash. If you set a default
value, than you'll never have a keyed value be empty (i.e. nil).
Consider further:

>> h = Hash.new
=> {}
>> puts 'empty' unless h[:test]
empty
=> nil


>> h = Hash.new('default value')

=> {}
>> puts 'empty' unless h[:test]
=> nil
>>

I think the confusion is that, in Chris' example, there's no assignment,
so the hash only holds the default value temporarily (i.e. just long
enough to not evaluate to nil or false).
--
Posted via http://www.ruby-forum.com/.

Yossef Mendelssohn

unread,
Apr 5, 2008, 3:07:02 AM4/5/08
to
On Apr 5, 12:46 am, Joshua Ballanco <jball...@gmail.com> wrote:
> Peña, Botp wrote:
> > From: Chris Shea [mailto:cms...@gmail.com]
> > # It's better to think of x ||= "ruby" as x || x = "ruby".
> > #
> > # --
> > # h = Hash.new('default value')
> > #
> > # h[:test] ||= 'assigned value'
> > # h # => {}
> > #
> > # # same as:
> > # h[:test] || h[:test] = 'assigned value'
> > # h # => {}
> > #
> > # # not:
> > # h[:test] = h[:test] || 'assigned value'
> > # h # => {:test=>"default value"}
>
> > i'd say that's a bug in design (unless x+=1 is now x+x=1 ;)
>
> > kind regards -botp
>
> Not at all. Rather, this is just a subtle misunderstanding of how hash
> is implemented.

<snip>

> The only reason that Chris' example behaves like "x || x = stuff" is
> because he's defined a default value for the hash. If you set a default
> value, than you'll never have a keyed value be empty (i.e. nil).
> Consider further:

<snip>

> I think the confusion is that, in Chris' example, there's no assignment,
> so the hash only holds the default value temporarily (i.e. just long
> enough to not evaluate to nil or false).
> --
> Posted viahttp://www.ruby-forum.com/.

There was a long thread about this some time ago, and I'm too tired to
search for it now. In the end, it came down a difference of opinion.

Some people say this violates their expectations because they're told
that `var op= value` translates to `var = var op value` always.

Other people say this is expected because `var ||= value` really means
`var = value unless var` and everybody should know that.

Personally, I am in the former camp. I think of the latter as an
optimization that works in many places, but optimizations are usually
a form of cheating and cheaters eventually get caught. In Ruby, this
cheater gets caught in the web of deceit centered around hashes with
default values.

Note that `hash_with_default_value[key] ||= val` is the only place
where this is even an issue.

--
-yossef

Peña, Botp

unread,
Apr 5, 2008, 4:25:46 AM4/5/08
to
From: Joshua Ballanco [mailto:jbal...@gmail.com]
# Not at all. Rather, this is just a subtle misunderstanding of
# how hash is implemented. Consider the following:
# >> h = Hash.new
# => {}
# >> h[:test] ||= 'testing without default'
# => "testing without default"
# >> h
# => {:test=>"testing without default"}
# >> h = Hash.new('default value')
# => {}
# >> h[:test] ||= 'testing with default'
# => "default value"
# >> h
# => {}
# The only reason that Chris' example behaves like "x || x = stuff" is
# because he's defined a default value for the hash. If you set
# a default value, than you'll never have a keyed value be empty
# (i.e. nil). Consider further:
# >> h = Hash.new
# => {}
# >> puts 'empty' unless h[:test]
# empty
# => nil
# >> h = Hash.new('default value')
# => {}
# >> puts 'empty' unless h[:test]
# => nil

my question is simple,

given,

h = Hash.new('default value')
#=> {}

why is

h[:test] ||= 'this is alternative'
#=> "default value"
h
#=> {}

different from

h[:test] = h[:test] || 'this is alternative'
#=> "default value"
h
#=> {:test=>"default value"}

??

That is not a nice surprise, imho; and the (syntax) sugar is not sweet :)

kind regards -botp

David A. Black

unread,
Apr 5, 2008, 6:13:27 AM4/5/08
to
Hi --

The false value isn't handled incorrectly, though. The test is for
boolean falseness, and both nil and false always pass that test. If
you need x to be a certain value (nil or any other), you should test
for exactly that value.

Also, it's not exactly that the lhs of ||= is nil. ||= will allow
uninitialized local variables, which are not nil:

irb(main):003:0> a ||= 1
=> 1
irb(main):004:0> b || b = 1
NameError: undefined local variable or method `b' for main:Object
from (irb):4

So we'd be getting into a thing with "uninitialized or nil, but not
false" which seems very use-case specific to me. I'd rather let the
boolean significance just do its thing.


David

--
Rails training from David A. Black and Ruby Power and Light:
ADVANCING WITH RAILS April 14-17 New York City
INTRO TO RAILS June 9-12 Berlin
ADVANCING WITH RAILS June 16-19 Berlin
See http://www.rubypal.com for details and updates!

Robert Dober

unread,
Apr 5, 2008, 8:24:43 AM4/5/08
to
I respectfully disagree, this is very well defined behavior, I
partially blame the list which *very* often explained to newbies that

@x ||= value

assignes value to @x when it was not defined before. It is sad that
things are incorrectly explained, that is all.
Furthermore things are simple

(1) lval ||= rval is lval = lval || rval
(2) nil || x = x
(3) false || x = x
(4) non initialized ivars evaluate to nil

once one accepts these basic rules of the language there is no surprise.
I like the syntactic sugar but to use a recently used quote "DE
GVSTIBVS NON DISPVTANDVM EST"

Cheers
Robert


--
http://ruby-smalltalk.blogspot.com/

---
Whereof one cannot speak, thereof one must be silent.
Ludwig Wittgenstein

David A. Black

unread,
Apr 5, 2008, 8:40:47 AM4/5/08
to
Hi --

On Sat, 5 Apr 2008, Robert Dober wrote:

> Furthermore things are simple
>
> (1) lval ||= rval is lval = lval || rval

I think you mean:

lval || lval = rval

(See the hash default case which flushes this out.)

Robert Dober

unread,
Apr 5, 2008, 9:26:17 AM4/5/08
to
On Sat, Apr 5, 2008 at 2:40 PM, David A. Black <dbl...@rubypal.com> wrote:
> Hi --
>
>
> On Sat, 5 Apr 2008, Robert Dober wrote:
>
>
> > Furthermore things are simple
> >
> > (1) lval ||= rval is lval = lval || rval
> >
>
> I think you mean:
>
> lval || lval = rval
>
> (See the hash default case which flushes this out.)
Wow David, as they say "A little knowledge is a dangerous thing", well
as I said ignorants like me teaching nonsense :(

Thx for correcting me David.

Cheers
Robert


>
>
>
>
> David
>
> --
> Rails training from David A. Black and Ruby Power and Light:
> ADVANCING WITH RAILS April 14-17 New York City
> INTRO TO RAILS June 9-12 Berlin
> ADVANCING WITH RAILS June 16-19 Berlin
> See http://www.rubypal.com for details and updates!
>
>

--

Avdi Grimm

unread,
Apr 5, 2008, 10:20:13 AM4/5/08
to
On Sat, Apr 5, 2008 at 6:13 AM, David A. Black

> The false value isn't handled incorrectly, though.

When I wrote "handled incorrectly", I didn't mean that Ruby handles it
incorrectly; I meant that I've seen a great deal of code people had
written which was incorrect in the face of explicit 'false' values
because of this very common misunderstanding of ||=.

> The test is for
> boolean falseness, and both nil and false always pass that test. If
> you need x to be a certain value (nil or any other), you should test
> for exactly that value.

I agree. Which is why I often use something like
if x.nil? then x = y end
which isn't nearly as concise and expressive as x ||= y. It could be
somewhat shortened to:
x.nil? && x = y
but I don't find that particularly readable. In any case, *neither*
of them conveniently handles the case where x is undefined as well as
the case where x is nil, the way ||= does, as you note below:

> Also, it's not exactly that the lhs of ||= is nil. ||= will allow
> uninitialized local variables, which are not nil:

[...]


> So we'd be getting into a thing with "uninitialized or nil, but not
> false" which seems very use-case specific to me.

The core of my argument is that this idiom isn't rare or use-case
specific at all. is, in my experience of both writing and reading Ruby
code, an *extremely* common pattern. Being able to concisely and
unambiguously say "after this line, x (or h[:x]) is guaranteed to be
initialized and non-nil, either by previous assignment or by the
specified default" is something I see a need for every working day.
And because it's such a common need, and because ||= seems so
deceptively close to fulfilling it, I have seen many, many examples of
code that was subtly incorrect because it used ||=. Ruby is a
language that often offers sugar for common idioms, and this is a very
common idiom.

--
Avdi

--
Avdi

Robert Klemme

unread,
Apr 5, 2008, 11:51:24 AM4/5/08
to
On 04.04.2008 21:41, Belorion wrote:
>> The operators are working with a three-valued logic. In a comparison,
>> a FalseClass object or a NilClass object will be logistically false.

Note though that "logistical" != "logical" - two quite different pairs
of shoes. :-)

Cheers

robert

Brian Adkins

unread,
Apr 5, 2008, 11:54:03 AM4/5/08
to
On Apr 5, 8:40 am, "David A. Black" <dbl...@rubypal.com> wrote:
> Hi --
>
> On Sat, 5 Apr 2008, Robert Dober wrote:
> > Furthermore things are simple
>
> > (1) lval ||= rval is lval = lval || rval
>
> I think you mean:
>
> lval || lval = rval

Huh?

My understanding has always been that x ||= y is shorthand for x = x
|| y just as x += y is shorthand for x = x + y. That also seems to be
the understanding of the "Programming Ruby" text (p. 125 of the
latest). In other words, an assignment will take place even if it's
superfluous.

Stefano Crocco

unread,
Apr 5, 2008, 12:04:50 PM4/5/08
to

I also thought so, but, according to "The ruby programming language", it's an
exception. It states that, if the left hand of var ||= something is not nil or
false, no assignment is performed and, if the left hand is an array element or
attribute, the setter method is not called.

Stefano+


David A. Black

unread,
Apr 5, 2008, 12:08:47 PM4/5/08
to
Hi --

Strictly speaking, it isn't shorthand for either, since there are
cases where the expansion will fail but ||= won't because x isn't
initialized. However, discounting that, the expansion is:

x || x = y

The only time this matters is with hashes that have default values. In
every other case, as far as I know, x = x || y also describes what's
happening. But the expansion which describes *every* case is x || x =
y.

I wrote a blog post about this recently:
http://dablog.rubypal.com/2008/3/25/a-short-circuit-edge-case
(I changed || to or for some reason, which screws up the precedence,
but I inserted corrections later.)

Joel VanderWerf

unread,
Apr 5, 2008, 1:57:21 PM4/5/08
to
David A. Black wrote:

> The only time this matters is with hashes that have default values. In
> every other case, as far as I know, x = x || y also describes what's
> happening. But the expansion which describes *every* case is x || x =
> y.

One other case (a very similar one) in which it matters is this:

class C
def x; @x || 5; end
def x=(v); puts "assigning @x=#{v}"; @x = v; end
end

c = C.new
c.x ||= 3 # no effect
p c.x # 5
c.x = 2 # assigning @x=2
p c.x # 2

--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

Rick DeNatale

unread,
Apr 5, 2008, 3:51:42 PM4/5/08
to
On Sat, Apr 5, 2008 at 12:08 PM, David A. Black <dbl...@rubypal.com> wrote:

> The only time this matters is with hashes that have default values. In
> every other case, as far as I know, x = x || y also describes what's
> happening.

Well here's another:

class Foo

attr_writer :x

def x
@x || 1 # !> instance variable @x not initialized
end

def inspect
"Foo(#{@x})" # !> instance variable @x not initialized
end
end

f = Foo.new
f.x # => 1
f # => Foo()
f.x ||= 2
f.x # => 1
f # => Foo()
f.x || f.x = 2
f.x # => 1
f # => Foo()
f.x = f.x || 2
f.x # => 1
f # => Foo(1)


--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

Robert Dober

unread,
Apr 5, 2008, 3:55:52 PM4/5/08
to
Hmm I am not sure I believe you rushed your example a little bit

c.x = c.x || a
and
c.x || c.x = a
seem to me having the same effect.

I only came up with a perverse example to show the difference
class C
count = 0
def x; true end
define_method :x= do |_|
count += 1
@x = count
end
end

c = C.new
c.x || c.x = 42
p c
c.x = c.x || 42
p c
c.x ||= 42
p c

David A. Black

unread,
Apr 5, 2008, 4:02:57 PM4/5/08
to
Hi --

On Sun, 6 Apr 2008, Robert Dober wrote:

> On Sat, Apr 5, 2008 at 7:57 PM, Joel VanderWerf <vj...@path.berkeley.edu> wrote:
>> David A. Black wrote:
>>
>>
>>> The only time this matters is with hashes that have default values. In
>>> every other case, as far as I know, x = x || y also describes what's
>>> happening. But the expansion which describes *every* case is x || x =
>>> y.
>>>
>>
>> One other case (a very similar one) in which it matters is this:
>>
>> class C
>> def x; @x || 5; end
>> def x=(v); puts "assigning @x=#{v}"; @x = v; end
>> end
>>
>> c = C.new
>> c.x ||= 3 # no effect
>> p c.x # 5
>> c.x = 2 # assigning @x=2
>> p c.x # 2
>>
>> --
>> vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407
>>
>>
> Hmm I am not sure I believe you rushed your example a little bit
>
> c.x = c.x || a
> and
> c.x || c.x = a
> seem to me having the same effect.

I don't think so:

irb(main):017:0> c = C.new
=> #<C:0x87d0c>
irb(main):018:0> c.x || c.x = 1 # 5 || c.x = 1 => 5
=> 5
irb(main):019:0> c.instance_eval { @x }
=> nil
irb(main):020:0> c.x = c.x || 1 # c.x = 5 || 1 => c.x = 5
assigning @x=5
=> 5
irb(main):021:0> c.instance_eval { @x }
=> 5

Robert Dober

unread,
Apr 5, 2008, 4:39:42 PM4/5/08
to
that is true but that point was difficult to see, and I preferred an
example which shows the difference from the "normal" object interface
although my code is of course perverted.

> => nil
> irb(main):020:0> c.x = c.x || 1 # c.x = 5 || 1 => c.x = 5
> assigning @x=5
> => 5
> irb(main):021:0> c.instance_eval { @x }
> => 5
>
>
>
>
> David
>
> --
> Rails training from David A. Black and Ruby Power and Light:
> ADVANCING WITH RAILS April 14-17 New York City
> INTRO TO RAILS June 9-12 Berlin
> ADVANCING WITH RAILS June 16-19 Berlin
> See http://www.rubypal.com for details and updates!
>
>

--

Todd Benson

unread,
Apr 5, 2008, 5:33:06 PM4/5/08
to

That's being a little nitpicky. I think either word works in this
case, but I understand what you meant :-)

Todd

Robert Klemme

unread,
Apr 6, 2008, 5:00:27 AM4/6/08
to
On 05.04.2008 23:33, Todd Benson wrote:
> On Sat, Apr 5, 2008 at 10:55 AM, Robert Klemme
> <short...@googlemail.com> wrote:
>> On 04.04.2008 21:41, Belorion wrote:
>>
>>>> The operators are working with a three-valued logic. In a comparison,
>>>> a FalseClass object or a NilClass object will be logistically false.
>>>>
>> Note though that "logistical" != "logical" - two quite different pairs of
>> shoes. :-)
>
> That's being a little nitpicky.

Absolutely!

> I think either word works in this
> case, but I understand what you meant :-)

If by "works" you mean "can be understood" then, yes. If you say that
the word can be placed there from a language point of view, also yes. I
do believe though that "logistically" is semantically misplaced here. :-))

Cheers

robert

Robert Dober

unread,
Apr 6, 2008, 5:01:59 AM4/6/08
to
and one cannot see the problem through the object interface either :(
In an attempt to make things clear enough I will take Joel's code again:

class << C = Class::new


def x; @x || 5 end

def get_x; @x end
def x= v; @x=v end
end

C.get_x --> nil
C.x ||= 42
C.get_x --> nil
C.x || C.x = 42
C.get_x --> nil
C.x = C.x || 42
C.get_x --> 5

Hurray I guess I finally got it.
Thx for the enlightenment.

Cheers
Robert

Brian Adkins

unread,
Apr 8, 2008, 11:42:09 AM4/8/08
to
On Apr 5, 12:08 pm, "David A. Black" <dbl...@rubypal.com> wrote:
> >> I think you mean:
>
> >> lval || lval = rval
>
> > Huh?
>
> > My understanding has always been that x ||= y is shorthand for x = x
> > || y just as x += y is shorthand for x = x + y. That also seems to be
> > the understanding of the "Programming Ruby" text (p. 125 of the
> > latest). In other words, an assignment will take place even if it's
> > superfluous.
>
> Strictly speaking, it isn't shorthand for either, since there are
> cases where the expansion will fail but ||= won't because x isn't
> initialized. However, discounting that, the expansion is:
>
> x || x = y
>
> The only time this matters is with hashes that have default values. In
> every other case, as far as I know, x = x || y also describes what's
> happening. But the expansion which describes *every* case is x || x =
> y.
>
> I wrote a blog post about this recently:http://dablog.rubypal.com/2008/3/25/a-short-circuit-edge-case
> (I changed || to or for some reason, which screws up the precedence,
> but I inserted corrections later.)

Interesting - nice blog post. That is surprising, inconsistent and
unfortunate :(

0 new messages