Forum: Ruby '=||'

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
B2969ce4f044f9f0a2f056d7264f68a7?d=identicon&s=25 James B. (byrnejb)
on 2009-05-05 21:50
Can someone point out to me where exactly in the API I find a discussion
of the '=||' operator?  Or am I imagining things and this does not
really exist?
B2969ce4f044f9f0a2f056d7264f68a7?d=identicon&s=25 James B. (byrnejb)
on 2009-05-05 21:54
James Byrne wrote:
> Or am I imagining things and this does not really exist?

Maybe I should look for '||=' instead. Sorry.
Eleanor McHugh (Guest)
on 2009-05-05 21:54
(Received via mailing list)
On 5 May 2009, at 20:51, James Byrne wrote:
> Can someone point out to me where exactly in the API I find a
> discussion
> of the '=||' operator?  Or am I imagining things and this does not
> really exist?


Are you referring to ||= ? If so it's one of the augmented assignment
operators so you won't find it documented separately as it's syntactic
sugar for:

x = x || some_other_value


Ellie

Eleanor McHugh
Games With Brains
http://slides.games-with-brains.net
----
raise ArgumentError unless @reality.responds_to? :reason
Joe Gutierrez (Guest)
on 2009-05-05 21:56
(Received via mailing list)
Here's a link for Ruby's operator expressions

http://phrogz.net/ProgrammingRuby/language.html

Joseph Gutierrez
Web Developer - Inc21
jgutierrez@inc21.com
5989a003d261c0abe6d08570ff6cc3d0?d=identicon&s=25 7stud -. (7stud)
on 2009-05-06 01:09
Eleanor McHugh wrote:
> Are you referring to ||= ? If so it's one of the augmented assignment
> operators so you won't find it documented separately as it's syntactic
> sugar for:
>
> x = x || some_other_value
>
>

Nope.

h = Hash.new(10)

h["red"] = h["red"] || 20

--output:--
{"red"=>10}


h = Hash.new(10)
h["blue"] ||= 20
p h

--output:--
{}


The statement:

x ||= val

is actually equivalent to:

x = val unless x
Eleanor McHugh (Guest)
on 2009-05-06 02:45
(Received via mailing list)
On 6 May 2009, at 00:09, 7stud -- wrote:
> h = Hash.new(10)
>
> x = val unless x
It seems you've misunderstood what happens under the hood when using
augmented assignment with tables as '||=' then becomes syntactic sugar
for 'x[] = x[] || some_other_value' and the assignment is performed
via '[]=' rather than '='. '[]=' will not create a key if it believes
it already exists and this is the cause of the behaviour you're seeing.

h = Hash.new(10)
p h["blue"]      => 10
h["blue"] ||= 20
p h        => {}

In this case when '||=' invokes the assignment it finds that h["blue"]
already contains a value because of the default so the hash method
'[]=' doesn't attempt to create a new key because it appears that the
key already exists.

Contrast this to:

h = Hash.new(10)
p h["blue"]      => 10
h["blue"] = nil
p h        => { "blue" => nil }
h["blue"] ||= 10
p h        => { "blue" => 10 }
h["blue"] ||= 20
p h        => { "blue" => 10 }

Here the key has been explicitly set equal to nil and '||=' acts the
way we'd expect an augmented assignment to work with scalar types.

Finally if no default is set for the table:

h = {}
h["red"] ||= 10
p h        => {"red" => 10}

The key is always created as expected.


Ellie

Eleanor McHugh
Games With Brains
http://slides.games-with-brains.net
----
raise ArgumentError unless @reality.responds_to? :reason
E56b77bf3cfb4d21fbf04a5bf943a36e?d=identicon&s=25 Rick D. (rdenatale)
on 2009-05-06 03:32
(Received via mailing list)
On Tue, May 5, 2009 at 8:45 PM, Eleanor McHugh
<eleanor@games-with-brains.com> wrote:
>> Nope.
...
> augmented assignment with tables as '||=' then becomes syntactic sugar for
> 'x[] = x[] || some_other_value' and the assignment is performed via '[]='
> rather than '='. '[]=' will not create a key if it believes it already
> exists and this is the cause of the behaviour you're seeing.

No, if x is truthy then

  x ||= expression

will NOT do any assignment.

The real equivalent to x ||= y

is

x || x = y

The assignment is short-circuited.

For the proof see:

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


--
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale
5989a003d261c0abe6d08570ff6cc3d0?d=identicon&s=25 7stud -. (7stud)
on 2009-05-06 03:38
Eleanor McHugh wrote:
> On 6 May 2009, at 00:09, 7stud -- wrote:
>> h = Hash.new(10)
>>
>> x = val unless x
> It seems you've misunderstood what happens under the hood when using
> augmented assignment with tables as '||=' then becomes syntactic sugar
> for 'x[] = x[] || some_other_value' and the assignment is performed
> via '[]=' rather than '='. '[]=' will not create a key if it believes
> it already exists and this is the cause of the behaviour you're seeing.
>
> h = Hash.new(10)
> p h["blue"]      => 10
> h["blue"] ||= 20
> p h        => {}
>
> In this case when '||=' invokes the assignment it finds that h["blue"]
> already contains a value because of the default so the hash method
> '[]=' doesn't attempt to create a new key because it appears that the
> key already exists.
>
> Contrast this to:
>
> h = Hash.new(10)
> p h["blue"]      => 10
> h["blue"] = nil
> p h        => { "blue" => nil }
> h["blue"] ||= 10
> p h        => { "blue" => 10 }
> h["blue"] ||= 20
> p h        => { "blue" => 10 }
>
> Here the key has been explicitly set equal to nil and '||=' acts the
> way we'd expect an augmented assignment to work with scalar types.
>
> Finally if no default is set for the table:
>
> h = {}
> h["red"] ||= 10
> p h        => {"red" => 10}
>
> The key is always created as expected.
>


Why do I need to care about what's going on under the hood?   If two
things
produce different results, then how can one be considered syntactic
sugar for the other.   In my opinion, "syntactic sugar" means that the
two formats can
be used interchangeably--with experienced programmers naturally
gravitating to the shorter, easier to type format, and inexperienced
programmers preferring the longer, but easier to understand format.
Eleanor McHugh (Guest)
on 2009-05-06 12:46
(Received via mailing list)
On 6 May 2009, at 02:38, 7stud -- wrote:
> Why do I need to care about what's going on under the hood?

Because when things don't work the way you expect, looking under the
hood allows you to adjust your expectations ;)

> If two
> things
> produce different results, then how can one be considered syntactic
> sugar for the other.   In my opinion, "syntactic sugar" means that the
> two formats can
> be used interchangeably--with experienced programmers naturally
> gravitating to the shorter, easier to type format, and inexperienced
> programmers preferring the longer, but easier to understand format.

All that syntactic sugar means is that one phrase is equivalent to
another syntactically, not that they're semantically equivalent.
However in the case of 'x = x || y' and 'x[n] = x[n] || y' are they
even syntactically equivalent? No. They appear syntactically similar,
but one invokes the '=' assignment operator whereas the other sends
the '[]=' message to the receiver. And because '[]=' leaves the
semantics of assignment to the receiver, that allows for the
differences you're seeing in this case.


Ellie

Eleanor McHugh
Games With Brains
http://slides.games-with-brains.net
----
raise ArgumentError unless @reality.responds_to? :reason
Eleanor McHugh (Guest)
on 2009-05-06 12:56
(Received via mailing list)
On 6 May 2009, at 02:31, Rick DeNatale wrote:
>>
> No, if x is truthy then
>
> The assignment is short-circuited.
>
> For the proof see:
>
> http://talklikeaduck.denhaven2.com/2008/04/26/x-y-redux

Yes, for assignment that's the case. But 'x[n] ||= y' isn't an
instance of assignment in that case as can easily be demonstrated:

class Test
   def method_missing symbol, *args
     puts "calling method #{symbol}"
   end
end

t = Test.new
t[:a] ||= 17

output:  calling method []
    calling method []=

Notice how even though method_missing returns a value and is thus
'true' the sequence still attempts all parts of the expression, and

t[:a] = t[:a] || 17

output:  calling method []
    calling method []=
    => 17

confirms that no short-circuited evaluation occurs.


Ellie

Eleanor McHugh
Games With Brains
http://slides.games-with-brains.net
----
raise ArgumentError unless @reality.responds_to? :reason
Shot (Piotr Szotkowski) (Guest)
on 2009-05-06 13:51
(Received via mailing list)
Eleanor McHugh:

> class Test
>   def method_missing symbol, *args
>     puts "calling method #{symbol}"
>   end
> end

> t = Test.new
> t[:a] ||= 17

> output:
> calling method []
>  calling method []=

> Notice how even though method_missing
> returns a value and is thus 'true'

No, puts returns nil.

> the sequence still attempts all parts of the expression, and

> t[:a] = t[:a] || 17

> output:
> calling method []
>  calling method []=
>  => 17

> confirms that no short-circuited evaluation occurs.

class Test
  def method_missing symbol, *args
    puts "calling method #{symbol}"
    true
  end
end

t = Test.new

t[:a] ||= 17
# calling method []

t[:a] = t[:a] || 17
# calling method []
# calling method []=

— Shot
E56b77bf3cfb4d21fbf04a5bf943a36e?d=identicon&s=25 Rick D. (rdenatale)
on 2009-05-06 14:04
(Received via mailing list)
On Wed, May 6, 2009 at 6:54 AM, Eleanor McHugh
<eleanor@games-with-brains.com> wrote:
>>>>
>>> exists and this is the cause of the behaviour you're seeing.
>>
>
>                calling method []=
> confirms that no short-circuited evaluation occurs.
>

But this is because the call to the missing [] method goes to the
method missing method which returns nil.

Remember that the assertion is that

t[:a] ||= 17

is equivalent to

(t[:a]) || (t[:a] = 17)

which in turn is equivalent to:

((t.[](:a)) || (t.[]=(:a, 17))

The output of your example shows both the :[] and :[]= methods are being
sent.

Try this variant:

class Test
 def method_missing symbol, *args
   puts "calling method #{symbol}"
 end

 def [](a)
   puts "in [] method"
   a
 end
end

t = Test.new
t[:a] ||= 17

This produces the output;
in [] method

The short circuiting only happens if the lhs expression returns a
non-truthy value.

Now it's true (I think) that "a op= b" is the same as "a = a op b" in
C, Ruby ain't C.

Sometimes the stuff "under the hood" is a little more complicated than
it first appears. <G>

--
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale
E56b77bf3cfb4d21fbf04a5bf943a36e?d=identicon&s=25 Rick D. (rdenatale)
on 2009-05-06 14:07
(Received via mailing list)
On Wed, May 6, 2009 at 6:54 AM, Eleanor McHugh
<eleanor@games-with-brains.com> wrote:
> On 6 May 2009, at 02:31, Rick DeNatale wrote:
>>
>> On Tue, May 5, 2009 at 8:45 PM, Eleanor McHugh
>> <eleanor@games-with-brains.com> wrote:
>>>
>
> Notice how even though method_missing returns a value and is thus 'true' the
> sequence still attempts all parts of the expression, and

I should have mentioned in my reply that since your method missing
body consists of a single puts statement, and since puts returns nil,
that value is non-truthy

and

nil || x

is x which is why the []= gets sent in your example.

--
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale
Eleanor McHugh (Guest)
on 2009-05-06 15:44
(Received via mailing list)
On 6 May 2009, at 13:03, Rick DeNatale wrote:
> The short circuiting only happens if the lhs expression returns a
> non-truthy value.
>
> Now it's true (I think) that "a op= b" is the same as "a = a op b" in
> C, Ruby ain't C.

:)

> Sometimes the stuff "under the hood" is a little more complicated than
> it first appears. <G>


Very true, and we all learn new stuff by considering it. For example I
suspect quite a few people would be surprised by the following code:

class Test
   def initialize
     @switch = false
   end

   def [] x
     puts "[#{x}]"
     @switch = !@switch
   end

   def []= x, y
     puts "[#{x}] = #{y}"
   end
end

t = Test.new
t[:a] ||= 17
t[:a] ||= 17
t[:a] ||= 17

output:      [a]
        [a]
        [a]

here the '[]' call returns 'true' and 'false' by turns but in either
event the assignment fails to call '[]='...


Ellie

Eleanor McHugh
Games With Brains
http://slides.games-with-brains.net
----
raise ArgumentError unless @reality.responds_to? :reason
Eleanor McHugh (Guest)
on 2009-05-06 15:45
(Received via mailing list)
On 6 May 2009, at 12:50, Shot (Piotr Szotkowski) wrote:
> Eleanor McHugh:

> No, puts returns nil.

doh! I knew that :(


Ellie

Eleanor McHugh
Games With Brains
http://slides.games-with-brains.net
----
raise ArgumentError unless @reality.responds_to? :reason
Eleanor McHugh (Guest)
on 2009-05-06 15:48
(Received via mailing list)
On 6 May 2009, at 13:05, Rick DeNatale wrote:
> I should have mentioned in my reply that since your method missing
> body consists of a single puts statement, and since puts returns nil,
> that value is non-truthy
>
> and
>
> nil || x
>
> is x which is why the []= gets sent in your example.

Perhaps I should have paid more attention to my morning email spam
regarding 'retrograde mercury'...


Ellie

Eleanor McHugh
Games With Brains
http://slides.games-with-brains.net
----
raise ArgumentError unless @reality.responds_to? :reason
E56b77bf3cfb4d21fbf04a5bf943a36e?d=identicon&s=25 Rick D. (rdenatale)
on 2009-05-06 16:00
(Received via mailing list)
On Wed, May 6, 2009 at 9:43 AM, Eleanor McHugh
<eleanor@games-with-brains.com> wrote:
>> Sometimes the stuff "under the hood" is a little more complicated than
>
> t = Test.new
> t[:a] ||= 17
> t[:a] ||= 17
> t[:a] ||= 17
>
> output:                 [a]
>                                [a]
>                                [a]
>
> here the '[]' call returns 'true' and 'false' by turns but in either event
> the assignment fails to call '[]='...


Are you sure?  When I run your code I get this:

[a]
[a]
[a] = 17
[a]



--
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale
Eleanor McHugh (Guest)
on 2009-05-06 16:23
(Received via mailing list)
On 6 May 2009, at 14:59, Rick DeNatale wrote:
> Are you sure?  When I run your code I get this:
>
> [a]
> [a]
> [a] = 17
> [a]

I typed '||' for '||=' when I ran that in irb, hence the 'mercury
retrograde' comment elsewhere...
Anyway it seems I'm suffering from an IAK meltdown today so it's
probably best if I step away from the keyboard before any more
innocent bytes get hurt :)


Ellie

Eleanor McHugh
Games With Brains
http://slides.games-with-brains.net
----
raise ArgumentError unless @reality.responds_to? :reason
This topic is locked and can not be replied to.