The ||= assignment operator

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

Nope ||= will assign if nil or false.

···

On Fri, Apr 4, 2008 at 1:11 PM, Belorion <belorion@gmail.com> 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:

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

--
"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)

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

Todd

···

On Fri, Apr 4, 2008 at 2:11 PM, Belorion <belorion@gmail.com> 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:

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

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.

···

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:

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

···

On Fri, Apr 4, 2008 at 2:28 PM, Todd Benson <caduceass@gmail.com> wrote:

On Fri, Apr 4, 2008 at 2:11 PM, Belorion <belorion@gmail.com> 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:
>
> 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

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

Todd

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

···

On Apr 4, 1:30 pm, Paul Mucur <mu...@waferbaby.com> wrote:

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.

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

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

···

On Fri, Apr 4, 2008 at 2:41 PM, Belorion <belorion@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.

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

···

From: Chris Shea [mailto:cmshea@gmail.com]
#
# --
# 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 :wink:

kind regards -botp

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

Cheers

  robert

···

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.

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

Peña, Botp wrote:

From: Chris Shea [mailto:cmshea@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 :wink:

kind regards -botp

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/\.

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

Todd

···

On Sat, Apr 5, 2008 at 10:55 AM, Robert Klemme <shortcutter@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. :slight_smile:

Cheers

        robert

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.

···

On Apr 4, 2008, at 8:53 PM, Avdi Grimm wrote:

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

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 :wink:

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

···

On Apr 5, 12:46 am, Joshua Ballanco <jball...@gmail.com> wrote:

--
-yossef

# 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 :slight_smile:

kind regards -botp

···

From: Joshua Ballanco [mailto:jballanc@gmail.com]

Hi --

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.

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

···

On Sat, 5 Apr 2008, Avdi Grimm wrote:

--
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!

HI --

Peña, Botp wrote:

From: Chris Shea [mailto:cmshea@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 :wink:

kind regards -botp

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

The only expansion that accounts for all cases of the ||= operator is
x || x = stuff. If it were actually x = x || stuff, then this:

irb(main):005:0> h = Hash.new(1)
=> {}
irb(main):006:0> h[:x] = h[:x] || 2
=> 1
irb(main):007:0> h
=> {:x=>1}

would behave the same as this:

irb(main):008:0> h[:y] ||= 2
=> 1
irb(main):009:0> h
=> {:x=>1}

but it doesn't. But ||= does behave like this:

irb(main):010:0> h[:z] || h[:z] = 2
=> 1
irb(main):011:0> h
=> {:x=>1}

In other words, x ||= y == x || x = y, or something :slight_smile:

I personally would like to see it act like x = x || y, though I think
hash defaults are the only case where the difference actually makes a
difference.

David

···

On Sat, 5 Apr 2008, Joshua Ballanco wrote:

--
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!

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. :slight_smile:

That's being a little nitpicky.

Absolutely!

I think either word works in this
case, but I understand what you meant :slight_smile:

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

···

On 05.04.2008 23:33, Todd Benson wrote:

On Sat, Apr 5, 2008 at 10:55 AM, Robert Klemme > <shortcutter@googlemail.com> wrote:

On 04.04.2008 21:41, Belorion wrote:

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

···

On Sat, Apr 5, 2008 at 10:25 AM, Peña, Botp <botp@delmonte-phil.com> wrote:

From: Joshua Ballanco [mailto:jballanc@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 :slight_smile:

kind regards -botp

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

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

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