'=||'

J

James Byrne

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

Eleanor McHugh

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
 
7

7stud --

Eleanor said:
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
 
E

Eleanor McHugh

Eleanor said:
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


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
 
R

Rick DeNatale

Nope. ...
The statement:

x ||= val

is actually equivalent to:

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.

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
 
7

7stud --

Eleanor said:
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.
 
E

Eleanor McHugh

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
 
E

Eleanor McHugh

The statement:

x ||= val

is actually equivalent to:

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.

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

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
 
R

Rick DeNatale

On 6 May 2009, at 00:09, 7stud -- wrote:

The statement:

x ||=3D val

is actually equivalent to:

x =3D val unless x


It seems you've misunderstood what happens under the hood when using
augmented assignment with tables as '||=3D' then becomes syntactic suga= r
for
'x[] =3D x[] || some_other_value' and the assignment is performed via '= []=3D'
rather than '=3D'. '[]=3D' will not create a key if it believes it alre= ady
exists and this is the cause of the behaviour you're seeing.

No, if x is truthy then

=A0x ||=3D expression

will NOT do any assignment.

The real equivalent to x ||=3D y

is

x || x =3D y

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] ||=3D y' isn't an instance= of
assignment in that case as can easily be demonstrated:

class Test
=A0def method_missing symbol, *args
=A0 =A0puts "calling method #{symbol}"
=A0end
end

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

output: calling method []
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0calling method []=3D

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] =3D t[:a] || 17

output: calling method []
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0calling method []=3D
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=3D> 17

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] ||=3D 17

is equivalent to

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

which in turn is equivalent to:

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

The output of your example shows both the :[] and :[]=3D 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 =3D Test.new
t[:a] ||=3D 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=3D b" is the same as "a =3D 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>

--=20
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
 
R

Rick DeNatale

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
 
E

Eleanor McHugh

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
 
E

Eleanor McHugh

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
 
R

Rick DeNatale

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

Now it's true (I think) that "a op=3D b" is the same as "a =3D a op b" i= n
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
=A0def initialize
=A0 =A0@switch =3D false
=A0end

=A0def [] x
=A0 =A0puts "[#{x}]"
=A0 =A0@switch =3D !@switch
=A0end

=A0def []=3D x, y
=A0 =A0puts "[#{x}] =3D #{y}"
=A0end
end

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

output: =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 [a]
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0[a]
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0[a]

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


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

[a]
[a]
[a] =3D 17
[a]



--=20
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
 
E

Eleanor McHugh

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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,902
Latest member
Elena68X5

Latest Threads

Top