Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why isn't `method=` treated the same as any other method?

Tags:

ruby

Consider the following code snippet:

class Example
  def my_attr=(value)
    @_my_attr = value
    @_my_attr * 3
  end
end

I expect the expression Example.new.my_attr = 5 to return 15, but that turns out to be wrong. The original return value is always returned, even when I call the = method explicitly:

Example.new.my_attr = 5 # => 5
Example.new.my_attr=(5) # => 5

How and why does Ruby do this? Does Ruby treat methods that end in = specially, or is it some other mechanism? I guess this precludes chaining on return values of = methods, right? Is there a way to make Ruby behave differently, or is this just how it is?

Update: Credit to @jeffgran for this:

Example.new.send(:my_attr=, 5) # => 15

This is a workaround, but on another level even more perplexing, since that would mean send is clearly not always equivalent in behavior to calling a method directly.

like image 701
door_number_three Avatar asked Sep 26 '13 16:09

door_number_three


People also ask

What is the difference between a function and a method amcat?

A function doesn't need any object and is independent, while the method is a function, which is linked with any object. We can directly call the function with its name, while the method is called by the object's name. Function is used to pass or return the data, while the method operates the data in a class.

What is the difference between methods Andconstructors?

A Constructor is a block of code that initializes a newly created object. A Method is a collection of statements which returns a value upon its execution. A Constructor can be used to initialize an object.

Whats the difference between a method and a function?

A method, like a function, is a set of instructions that perform a task. The difference is that a method is associated with an object, while a function is not.

What is the difference between functions and methods give an example of each?

Unlike a function, methods are called on an object. Like in our example above we call our method . i.e. “my_method” on the object “cat” whereas the function “sum” is called without any object. Also, because the method is called on an object, it can access that data within it.


1 Answers

This is how assignment works; the return value is ignored, and the result of an assignment expression is always the right-hand value. This is a fundamental feature of Ruby's grammar. left-hand side = right-hand side will always evaluate to right-hand side, regardless of whether left hand side is a variable (x), a method (object.x), a constant (X) or any expression.

Source: Programming Languages | Ruby IPA Ruby Standardization WG Draft, 11.4.2.2.5, Single method assignments


Consider chaining of assignments, x = y = 3.

For this to work correctly, the result of y = 3 must be 3, regardless of the actual value returned by the y= method. x = y = 3 is meant to read as y = 3; x = 3, not as y = 3; x = y which is what would be implied if the return value from y= was treated as the result of y = 3.

Or consider all the other places assignment can be used. Sometimes, instead of this...

obj.x = getExpensiveThing()
if obj.x 
  ...

... we write this ...

if obj.x = getExpensiveThing()

This couldn't work if the result of obj.x = ... could be any arbitrary thing, but we know it will work because the result of obj.x = y is always y.

Update

A comment on the question states:

Interesting, I wasn't aware of this scenario. It seems that method= returns whatever input is given...

No, it's an important distinction to make. This has nothing to do with the return value of method assignment, and it definitely does not "return whatever input is given", it returns whatever you tell it to return.

The whole point is that the return value is ignored by the grammar of the language; assignment doesn't evaluate to the return value of the attr= method, but the return value still exists as evidenced by the question itself: Example.new.send(:my_attr=, 5) # => 15. This works because it is not assignment. You're side-stepping that part of the Ruby language.

Update again

To be clear: x and y in my examples shouldn't be interpreted as literal Ruby variables, they are place holders for any valid left-hand side of an assignment. x or y could be any expression: a, obj.a, CONSTANT_A, Something::a, @instance_a, it's all the same. The value of assignment is always the right-hand side.

like image 62
meagar Avatar answered Nov 16 '22 03:11

meagar