

Are you an advanced rubyist? - Denzel

Try to do this by memory without using a ruby interpreter.<p>Given:<p><pre><code>  class Test
    def attr
      @attr
    end

    def attr=(value)
      @attr = value * 2
    end
  end
</code></pre>
Evaluate:<p><pre><code>  t = Test.new
  t.attr = 5 # Returns =&#62; ?
  t.attr # Returns =&#62; ?
</code></pre>
I don't consider myself an advanced rubyist, and I couldn't answer this until today. Nonetheless, the behavior surprised me. I've read a number of books about ruby and never seen this behavior mentioned; in fact, most deem a setter method as just another method. Any explanation for this behavior?
======
chc
It's not the method that's special, per se, but the `=` operator. The `=`
operator always returns the right operand, discarding whatever is returned
from the setter method. AFAIK it's because that's the expected behavior when
you're doing chained assignments. You wouldn't normally want the leftmost
operand in `a = b = c` to get a weird value just because the middle operand
internally does some transformation, so the `=` operator yields the right
operand unaltered.

If you went directly through the method (e.g. `t.send(:attr=, 5)` or
`t.method(:attr=).call 5`), you'd get 10.

~~~
Denzel
Thanks for the explanation chc, and grn. Indeed, invoking the method
indirectly with send or method does produce 10. Nice!

------
grn
It's related to the right-associativity of the assignment operator and its
expected semantics. If you write

    
    
        a = b = c
    

then it is interpreted as

    
    
        a = (b = c)
    

When c = 5 then you expect that

    
    
        a = b = 5 # which is equivalent to a = (b = 5)
    

results in a and b set to 5. That's why = must return its right-hand side.

~~~
Denzel
Agreed. I understand exactly what you're saying, and I don't dispute the
semantics.

I guess my only problem is that in this case "t.attr" isn't really _equal_ to
5, therefore making

    
    
      a = t.attr = 5
    

differ from

    
    
      t.attr = 5
      a = t.attr
    

which to a novice would appear equivalent. 99.9% of the time you wouldn't
expect assignment to have side-effects. But in my eyes Ruby gives you so much
power to use responsibly, it appears out of character to disallow it in this
instance. (Note: I had to forgo a little trick with ActiveRecord that would've
reduced duplication because of this.)

In the grand scheme of things, it's not a big deal. But it was very
interesting to me. :)

~~~
chc
The alternative is making

    
    
      a = t.attr = 5
    

different from

    
    
      t.attr = 5
      a = 5
    

which is most often what someone means when they write a chained assignment.
It's all part of the "principle of least surprise" that Rubyists used to tout
so much — but unfortunately, different people find different things
surprising.

