||= with 1.8 and 1.9?

A friend of mine on Twitter recently posted this tidbit of code:

class OrOrEquals
  def test
    @test
  end

  def test=(test)
    @test = test
    'not test'
  end
end

p (OrOrEquals.new.test = 'test')
# ruby 1.8 returns 'test'
# ruby 1.9 returns 'test'

p (OrOrEquals.new.test ||= 'test')
# ruby 1.8 returns 'test'
# ruby 1.9 returns 'not test'

It works as indicated. Is this -normal- behavior ?

···

--
Posted via http://www.ruby-forum.com/.

Aldric Giacomoni wrote:

It works as indicated. Is this -normal- behavior ?

FWIW, I get identical results to you using

ruby 1.8.6 (2007-09-24 patchlevel 111) [i486-linux]
ruby 1.9.2dev (2009-07-18 trunk 24186) [i686-linux]

In ruby 1.8, an attribute writer method (:foo=) always(*) returns the
value of its argument, rather than the last value calculated in the
method, so that you can tell by inspection that

   a = foo.bar = 123

will set a to 123. Perhaps it has been decided to change that for 1.9.

(*) Strangely, even 1.8 doesn't work that way if you use send().

RUBY_VERSION

=> "1.8.6"

class Foo; def bar=(x); 123; end; end

=> nil

Foo.new.bar = 999

=> 999

Foo.new.send(:bar=, 999)

=> 123

···

--
Posted via http://www.ruby-forum.com/\.

Not only that, this odd behaviour only happens for ||= and not for plain =

    $ cat orequal.rb
    puts RUBY_VERSION

    class OrOrEquals
      def test
        @test
      end

      def test=(test)
        @test = test
        'not test'
      end
    end

    o = OrOrEquals.new
    p o.test = 'a test'
    o.test = nil
    p o.test ||= 'a test'

    $ ruby orequal.rb
    1.8.7
    "a test"
    "a test"

    $ ruby19 orequal.rb
    1.9.1
    "a test"
    "not test"

···

At 2009-08-26 11:00AM, "Aldric Giacomoni" wrote:

A friend of mine on Twitter recently posted this tidbit of code:

class OrOrEquals
   def test
     @test
   end

   def test=(test)
     @test = test
     'not test'
   end
end

p (OrOrEquals.new.test = 'test')
# ruby 1.8 returns 'test'
# ruby 1.9 returns 'test'

p (OrOrEquals.new.test ||= 'test')
# ruby 1.8 returns 'test'
# ruby 1.9 returns 'not test'

It works as indicated. Is this -normal- behavior ?

--
Glenn Jackman
    Write a wise saying and your name will live forever. -- Anonymous

Brian Candler wrote:

Aldric Giacomoni wrote:

It works as indicated. Is this -normal- behavior ?

FWIW, I get identical results to you using

ruby 1.8.6 (2007-09-24 patchlevel 111) [i486-linux]
ruby 1.9.2dev (2009-07-18 trunk 24186) [i686-linux]

$ r -v
ruby 1.8.6 (2007-03-13 patchlevel 0) [i686-darwin8.11.1]
$ r9 -v
ruby 1.9.1p243 (2009-07-16 revision 24175) [i386-darwin8.11.1]

$ r r1test.rb
"test"
"test"

$ r9 r1test.rb
"test"
"not test"

In ruby 1.8, an attribute writer method (:foo=) always(*) returns the
value of its argument, rather than the last value calculated in the
method, so that you can tell by inspection that

   a = foo.bar = 123

will set a to 123. Perhaps it has been decided to change that for 1.9.

If so, then David Black didn't know about it:

···

-------
WARNING Setter methods don't return what you might think. When you
use the syntactic sugar that lets you make calls to = methods look like
assignments, Ruby takes the assignment semantics seriously. Assignments
(like x = 1) evaluate to whatever's on their right-hand side. [On the
other hand,] [m]ethods usually return the value of the last expression
evaluated during execution. But = method calls behave like assignments:
the value of the expression ticket.price = 63.00 is 63.00, even if the
ticket= method returns the string "Ha ha!" The idea is to keep the
semantics consistent. Under the hood, it's a method call; but it looks
like an assignment and behaves like an assignment with respect to its
value as an expression.
------
p. 72, The Well Grounded Rubyist

------
A word on Ruby versions

The Well Grounded Rubyist is about version 1.9.1 of the Ruby language,
the most recent version.
------
p xxvii, The Well Grounded Rubyist

--
Posted via http://www.ruby-forum.com/\.

actually it's not that an attribute writer method always returns the
argument it's that a chained assignment

a = b = c

is treated the same as

b = c; a = c

in other words the result of the b= is ignored.

That's why sending :bar= is giving the result of the method.

As for why Ruby1.9 is treating the ||= form differently, I haven't
figured that out.

···

On Wed, Aug 26, 2009 at 11:08 AM, Brian Candler<b.candler@pobox.com> wrote:

In ruby 1.8, an attribute writer method (:foo=) always(*) returns the
value of its argument, rather than the last value calculated in the
method, so that you can tell by inspection that

a = foo.bar = 123

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

In ruby 1.8, an attribute writer method (:foo=) always(*) returns the
value of its argument, rather than the last value calculated in the
method, so that you can tell by inspection that

   a = foo.bar = 123

will set a to 123. Perhaps it has been decided to change that for 1.9.

If so, then David Black didn't know about it:

Whoops. I didn't see the change from = to ||= in the code. So nothing
has changed from 1.8.6 to 1.9.1 with respect to = methods. The change
is in the behavior of ||=. Those quotes from the Well
Grounded Rubyist don't apply to ||=.

···

--
Posted via http://www.ruby-forum.com/\.

I know what David is getting at here, but that first sentence isn't
exactly true, although I'll grant a pedagogical license for it.

It's not that the return value isn't what you think, it's that it's
ignored when the setter method is called from the sugary syntax of an
assignment. Note that he says that ticket.price = 63.00 evaluates to
63.00 "even if the ticket= method returns the string "Ha Ha!". Which
is different than saying ticket= returns 63.00 instead of "what you
might think."

And Brian's example of using send demonstrates that it's the
assignment and not the method call result which isn't what you think.

···

On Wed, Aug 26, 2009 at 12:15 PM, 7stud --<bbxx789_05ss@yahoo.com> wrote:

If so, then David Black didn't know about it:

-------
WARNING Setter methods don't return what you might think. When you
use the syntactic sugar that lets you make calls to = methods look like
assignments, Ruby takes the assignment semantics seriously. Assignments
(like x = 1) evaluate to whatever's on their right-hand side. [On the
other hand,] [m]ethods usually return the value of the last expression
evaluated during execution. But = method calls behave like assignments:
the value of the expression ticket.price = 63.00 is 63.00, even if the
ticket= method returns the string "Ha ha!" The idea is to keep the
semantics consistent. Under the hood, it's a method call; but it looks
like an assignment and behaves like an assignment with respect to its
value as an expression.
------
p. 72, The Well Grounded Rubyist

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

Rick Denatale wrote:

As for why Ruby1.9 is treating the ||= form differently, I haven't
figured that out.

I always thought that a ||= b was just a shortcut for a = a || b, but it
looks like that's not necessarily true in 1.9.

···

--
Posted via http://www.ruby-forum.com/\.

Hi,

a = b = c

is treated the same as

b = c; a = c

in other words the result of the b= is ignored.

No,

  a = b = c

is parsed as

  a = (b = c)

so, result of the latter assignment is used for the former assignment.
The point is when b here is an attribute assignment (e.g. foo.b = c),
the result value from the setter method (b=) is ignored.

···

In message "Re: ||= with 1.8 and 1.9 ?" on Thu, 27 Aug 2009 01:20:20 +0900, Rick DeNatale <rick.denatale@gmail.com> writes:

That's why sending :bar= is giving the result of the method.

As for why Ruby1.9 is treating the ||= form differently, I haven't
figured that out.

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

Rick Denatale wrote:

ticket= method returns the string "Ha ha!" �The idea is to keep the
semantics consistent. �Under the hood, it's a method call; but it looks
like an assignment and behaves like an assignment with respect to its
value as an expression.
------
p. 72, The Well Grounded Rubyist

I know what David is getting at here, but that first sentence isn't
exactly true, although I'll grant a pedagogical license for it.

It's not that the return value isn't what you think, it's that it's
ignored when the setter method is called from the sugary syntax of an
assignment.

It sounds like you are trying to make a distinction between the sugared
syntax and the normal method call syntax, but...

class A
  def x
    @x
  end

  def x=(val)
    @x = val
    "Ha, ha!"
  end
end

a1 = A.new
puts a1.x = 10

a2 = A.new
puts a2.x=(10)

--output:--
10
10

both versions return the same thing.

Note that he says that ticket.price = 63.00 evaluates to
63.00 "even if the ticket= method returns the string "Ha Ha!". Which
is different than saying ticket= returns 63.00 instead of "what you
might think."

So are you faulting his use of the phrase "returns the string "Ha, ha!"
because the method doesn't actually return "Ha, ha!"--it returns 63.00?

···

On Wed, Aug 26, 2009 at 12:15 PM, 7stud --<bbxx789_05ss@yahoo.com> > wrote:

--
Posted via http://www.ruby-forum.com/\.

Brian Candler wrote:

I always thought that a ||= b was just a shortcut for a = a || b, but it looks like that's not necessarily true in 1.9.

It's never been exactly the same in 1.8, either.

h = Hash.new(0)
h[1] ||= 2 # this...
h[3] = h[3] || 4 # is not like this...
h[5] || h[5] = 6 # but more like this.
p h # {3=>0}

http://groups.google.com/group/comp.lang.ruby/browse_thread/thread/47f273f6ab57303e/291babfc79b4b822

···

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

Hi,

>a = b = c
>
> is treated the same as
>
>b = c; a = c
>
>in other words the result of the b= is ignored.

No,

a = b = c

is parsed as

a = (b = c)

Yes, I was speaking a bit loosely and was trying to say just what you said next

so, result of the latter assignment is used for the former assignment.
The point is when b here is an attribute assignment (e.g. foo.b = c),
the result value from the setter method (b=) is ignored.

And I think that the really important point here is that

a.b ||= c

should have have the same effect as

a.b || a.b = c

And that it's surprising that in Ruby 1.9 it doesn't

puts RUBY_VERSION

class OrOrEquals
  def test
    @test
  end

  def test=(test)
    @test = test
    'not test'
  end
end

o = OrOrEquals.new

direct = o.test = 'a test'
p direct
o.test = nil
with_oror = o.test ||= 'a test'
p with_oror

with_or_or_assign = o.test || o.test = "a test"
p with_or_or_assign

$ multiruby or_or_equals.rb
/opt/local/lib/ruby/site_ruby/1.8/rubygems/source_index.rb:144:
warning: /Users/rick/.gem/ruby/1.8/specifications: Permission denied

VERSION = 1.8.6-p368
CMD = ~/.multiruby/install/1.8.6-p368/bin/ruby or_or_equals.rb

1.8.6
"a test"
"a test"
"a test"

RESULT = 0

VERSION = 1.8.7-p160
CMD = ~/.multiruby/install/1.8.7-p160/bin/ruby or_or_equals.rb

1.8.7
"a test"
"a test"
"a test"

RESULT = 0

VERSION = 1.9.1-p0
CMD = ~/.multiruby/install/1.9.1-p0/bin/ruby or_or_equals.rb

1.9.1
"a test"
"not test"
"a test"

RESULT = 0

···

On Wed, Aug 26, 2009 at 7:00 PM, Yukihiro Matsumoto<matz@ruby-lang.org> wrote:

In message "Re: ||= with 1.8 and 1.9 ?" > on Thu, 27 Aug 2009 01:20:20 +0900, Rick DeNatale <rick.denatale@gmail.com> writes:

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

The issue is with the ||= operator though! Did the way it is use change,
from doing an assignment to sending a value to the 'assign' method, or
something like that?

I mean, ||= is / was understood to be an assignment operator, wasn't it?
Did that change?

···

--
Posted via http://www.ruby-forum.com/.

Hi --

Rick Denatale wrote:

ticket= method returns the string "Ha ha!" ???The idea is to keep the
semantics consistent. ???Under the hood, it's a method call; but it looks
like an assignment and behaves like an assignment with respect to its
value as an expression.
------
p. 72, The Well Grounded Rubyist

I know what David is getting at here, but that first sentence isn't
exactly true, although I'll grant a pedagogical license for it.

It's not that the return value isn't what you think, it's that it's
ignored when the setter method is called from the sugary syntax of an
assignment.

It sounds like you are trying to make a distinction between the sugared
syntax and the normal method call syntax, but...

class A
def x
   @x
end

def x=(val)
   @x = val
   "Ha, ha!"
end
end

a1 = A.new
puts a1.x = 10

a2 = A.new
puts a2.x=(10)

--output:--
10

both versions return the same thing.

Note that he says that ticket.price = 63.00 evaluates to
63.00 "even if the ticket= method returns the string "Ha Ha!". Which
is different than saying ticket= returns 63.00 instead of "what you
might think."

So are you faulting his use of the phrase "returns the string "Ha, ha!"
because the method doesn't actually return "Ha, ha!"--it returns 63.00?

I think that's the sentence he feels I got right, as opposed to the
first one. His point is that you can use send to show what the method
is actually returning:

def x=(y); "hi"; end

=> nil

self.x = 1

=> 1

send(:x=,1)

=> "hi"

So that my first sentence should be "Calls to setter methods don't
always produce the results you might think they would" or something
like that.

David

···

On Thu, 27 Aug 2009, 7stud -- wrote:

On Wed, Aug 26, 2009 at 12:15 PM, 7stud --<bbxx789_05ss@yahoo.com> >> wrote:

--
David A. Black / Ruby Power and Light, LLC / http://www.rubypal.com
Q: What's the best way to get a really solid knowledge of Ruby?
A: Come to our Ruby training in Edison, New Jersey, September 14-17!
    Instructors: David A. Black and Erik Kastner
    More info and registration: http://rubyurl.com/vmzN

Rick Denatale wrote:

ticket= method returns the string "Ha ha!" �The idea is to keep the
semantics consistent. �Under the hood, it's a method call; but it looks
like an assignment and behaves like an assignment with respect to its
value as an expression.
------
p. 72, The Well Grounded Rubyist

I know what David is getting at here, but that first sentence isn't
exactly true, although I'll grant a pedagogical license for it.

It's not that the return value isn't what you think, it's that it's
ignored when the setter method is called from the sugary syntax of an
assignment.

It sounds like you are trying to make a distinction between the sugared
syntax and the normal method call syntax, but...

a1 = A.new
puts a1.x = 10

a2 = A.new
puts a2.x=(10)

both versions return the same thing.

Because they are both using the assignment sugar. Parenthesizing the
10 doesn't change that.

Note that he says that ticket.price = 63.00 evaluates to
63.00 "even if the ticket= method returns the string "Ha Ha!". Which
is different than saying ticket= returns 63.00 instead of "what you
might think."

So are you faulting his use of the phrase "returns the string "Ha, ha!"
because the method doesn't actually return "Ha, ha!"--it returns 63.00?

David already answered this one, he understood.

···

On Wed, Aug 26, 2009 at 12:49 PM, 7stud --<bbxx789_05ss@yahoo.com> wrote:

On Wed, Aug 26, 2009 at 12:15 PM, 7stud --<bbxx789_05ss@yahoo.com> >> wrote:

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

Joel VanderWerf wrote:

h = Hash.new(0)
h[1] ||= 2 # this...
h[3] = h[3] || 4 # is not like this...
h[5] || h[5] = 6 # but more like this.
p h # {3=>0}

Hashes have always been a kind of special case, especially when they
have a default value set up :slight_smile: We're talking about assignments on plain
variables, which are (I thought) more clear-cut.

···

--
Posted via http://www.ruby-forum.com/\.

Rick Denatale wrote:

a1 = A.new
puts a1.x = 10

a2 = A.new
puts a2.x=(10)

both versions return the same thing.

Because they are both using the assignment sugar. Parenthesizing the
10 doesn't change that.

The method name is 'x=' and it was called using 'obj.x='. Where's the
sugar?

···

--
Posted via http://www.ruby-forum.com/\.

Aldric Giacomoni wrote:

Joel VanderWerf wrote:

h = Hash.new(0)
h[1] ||= 2 # this...
h[3] = h[3] || 4 # is not like this...
h[5] || h[5] = 6 # but more like this.
p h # {3=>0}

Hashes have always been a kind of special case, especially when they have a default value set up :slight_smile: We're talking about assignments on plain variables, which are (I thought) more clear-cut.

True. The quirk happens with reader and writer methods as well as hashes (scroll down a bit in the google groups page to see some examples).

···

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

7stud -- wrote:

Rick Denatale wrote:

a1 = A.new
puts a1.x = 10

a2 = A.new
puts a2.x=(10)

both versions return the same thing.

Because they are both using the assignment sugar. Parenthesizing the
10 doesn't change that.

The method name is 'x=' and it was called using 'obj.x='. Where's the
sugar?

The parentheses in (10) are not delimiting arguments of a method call;
they are just bracketing a value expression. Note:

class Foo; def bar=(x,y,z); 123; end; end

=> nil

Foo.new.send(:bar=,7,8,9)

=> 123

Foo.new.bar=(7,8,9)

SyntaxError: compile error
(irb):3: syntax error, unexpected ',', expecting ')'
Foo.new.bar=(7,8,9)
               ^
  from (irb):3

Similarly:

Foo.new.bar= 7,8,9

ArgumentError: wrong number of arguments (1 for 3)
  from (irb):4:in `bar='
  from (irb):4

The other way you can see it's sugar is that a space is allowed between
x and =.

···

from :0
  from :0
--
Posted via http://www.ruby-forum.com/\.

Joel VanderWerf wrote:

True. The quirk happens with reader and writer methods as well as hashes
(scroll down a bit in the google groups page to see some examples).

Look at the original code, though..

class OrOrEquals
attr_accessor :test
  def test
    @test
  end

  def test=(test)
    @test = test
    'not test'
  end
end

p (OrOrEquals.new.test = 'test')
# ruby 1.8 returns 'test'
# ruby 1.9 returns 'test'

p (OrOrEquals.new.test ||= 'test')
# ruby 1.8 returns 'test'
# ruby 1.9 returns 'not test'

... And compare this to what's written in that g-group:

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

Well, then... It -IS- assigning something, isn't it, since there's no
value to @test ? Why does it return something other than the result of
the assignment?

···

--
Posted via http://www.ruby-forum.com/\.