Feature #10570
closedAllow `+=` etc. to be methods
Description
In MRI, it is currently possible to create #+= methods in C extensions and even += aliases for existing methods in Ruby source code. These methods are only callable via #send('+=', ...) because the parser interprets a += b as a = a + b before the "operator method" is called. Thus, a += b is currently equivalent to a = a.send('+', b) rather than a.send('+=', b). This feature request is to allow and support <OP>= methods, where <OP> is any of Ruby's operators that can be defined as methods (e.g. #+, #-, etc).
A related feature request would be to allow #<attribute><op>= methods in addition to the already supported#<attribute>= methods. As it is now, foo.bar += x is equivalent to foo.send('bar=', foo.send('bar').send('+', x)), which requires that the return value of foo.bar implements #+ and that foo implements #bar=. If this related feature were implemented, foo.bar += x would be equivalent to foo.send('bar+=', x)`.
I guess this would be tricky to add in a backwards compatible way. What happens if #+= is not defined for the would-be receiver? How would that condition be detected in a way that could fall back to the old default behavior?
What other possible complications could make this request impractical?
Updated by matz (Yukihiro Matsumoto) almost 11 years ago
- Status changed from Open to Rejected
a+=b is a short hand form of a = a + b by definition. It's fundamentally an assignment. Target of assignment (including += etc) is a variable, not an object. It cannot be implemented by a method.
For example, how can we define Integer#+=?
Matz.
Updated by david_macmahon (David MacMahon) almost 11 years ago
Thanks for your thoughtful reply!
Yukihiro Matsumoto wrote:
a+=bis a short hand form ofa = a + bby definition. It's fundamentally an assignment. Target of assignment (including += etc) is a variable, not an object. It cannot be implemented by a method.
I agree that a = a + b is fundamentally an assignment (and I'm not asking for an assignment operator :-)). I'm not sure that the definition of a += b as a short hand form of a = a + b is quite so fundamental. Definitions can be redefined.
What about a[i] = b? That is (or at least looks like) a form of assignment that is already implemented as a method.
For example, how can we define
Integer#+=?
Immutable classes would simply not define a += method. For example, we already have Fixnum#[] without Fixnum#[]=.
Dave
Updated by david_macmahon (David MacMahon) almost 11 years ago
David MacMahon wrote:
Yukihiro Matsumoto wrote:
For example, how can we define
Integer#+=?Immutable classes would simply not define a
+=method
...and the current behavior would be used for lvalues not implementing a += method. That's the tricky part I was referring to in the original post.
Dave
Updated by matz (Yukihiro Matsumoto) almost 11 years ago
Array#[]= can be defined by alternation of elements of an array, which does not contain any assignment to variables. That's the reason that can be implemented by a method. In contrast, a += b includes assignment in its semantic, especially in a language like Ruby that has uniform reference model (unlike languages like C++).
Besides that, replacing += etc by methods would slow down those assignments significantly. I don't want that neither.
Matz.
Updated by david_macmahon (David MacMahon) almost 11 years ago
Yukihiro Matsumoto wrote:
Array#[]=can be defined by alternation of elements of an array, which does not contain any assignment to variables. That's the reason that can be implemented by a method. In contrast,a += bincludes assignment in its semantic, especially in a language like Ruby that has uniform reference model (unlike languages like C++).Besides that, replacing
+=etc by methods would slow down those assignments significantly. I don't want that neither.
Fair enough, but then what about #<attr><op>= methods? it seems like foo.bar += 1 could be similar to the a[i] = b case because it is not assigning foo itself. Instead it could alter an attribute of foo by calling the #bar+= method. I guess supporting that would create problems for existing code that expects the current behavior. I'll stop pursuing this now.
Thanks again,
Dave