Operators +, += and = in Ruby 2

R

Robert Klemme

Hi all,

this occurred to me some days ago and I'd like to hear what others think.

Operator += can't be user defined but instead is extended to a combination
of "+" and assignment:
"x += y" becomes implicitely "x = x + y".

Now, wouldn't it be better to turn that around and have people define
operator += and automatically convert operator + like in:

"x + y" becomes implicitely "x.dup += y"

The reason I'm offering this is performance: "x = x + y" is slower than an
inplace manipulation (that += would do), which is often what's desired
here.

On the downside:

- This will likely break a lot of code already defined unless one would
allow to define "+" for a while (possibly with a warning).

- The change has subtle effects, i.e., people relying on "x += y" to
create a new instance and not changing the state of x might encounter
surprising effects.

- dup might or might not be the appropriate method to create a copy
depending on the class at hand. When defining operator +, we are currently
free in deciding what's the type of result of this operation. That
freedom would definitely go away.

What do others think? Did I overlook something?

Thx!

robert
 
S

Simon Strandgaard

On Wed, 25 Feb 2004 16:12:16 +0100, Robert Klemme wrote:
[snip]
What do others think? Did I overlook something?

It would be really nice to be able to overload '+=', so that #close can be
invoked correct.

server> ruby a.rb
0
1
server> expand -t2 a.rb
class A
@@count = 0
def initialize(value=nil)
@value = value || 0
@@count += 1
end
attr_reader :value
def close
@@count -= 1
end
def A.count
@@count
end
def +(value)
A.new(@value + value)
end
end

p A.count
a = A.new
a += 5
a.close
p A.count
server>
 
R

Robert Klemme

Simon Strandgaard said:
On Wed, 25 Feb 2004 16:12:16 +0100, Robert Klemme wrote:
[snip]
What do others think? Did I overlook something?

It would be really nice to be able to overload '+=', so that #close can be
invoked correct.

Your problem is, that += silently discards an instance without invoking
some kind of "destructor" and without the chance to detect this situation.
Is that correctly analyzed?

You could indeed deal with that when overriding of + and += was swapped:
in that case you'd only have to override #dup to increment the counter.
server> ruby a.rb
0
1
server> expand -t2 a.rb
class A
@@count = 0
def initialize(value=nil)
@value = value || 0
@@count += 1
end
attr_reader :value
def close
@@count -= 1
end
def A.count
@@count
end
def +(value)
A.new(@value + value)
end
end

p A.count
a = A.new
a += 5
a.close
p A.count
server>

Kind regards

robert
 
R

Robert Klemme

Dennis Ranke said:
Now, wouldn't it be better to turn that around and have people define
operator += and automatically convert operator + like in:

"x + y" becomes implicitely "x.dup += y"

[...]

What do others think? Did I overlook something?

Yes, I think so. With your proposed change it would be impossible to
express operations where the result has a different type than the first
operand, like, for example:

Float = Vector * Vector # (dot product)
or
Vector = Matrix * Vector

I didn't overlook that one. It was in the first post already:

<quote>
On the downside:
[...]
- dup might or might not be the appropriate method to create a copy
depending on the class at hand. When defining operator +, we are currently
free in deciding what's the type of result of this operation. That
freedom would definitely go away.
</quote>

A solution would be to provide another method (maybe #copy or #op_dup or
similar) that defaults to #dup but can be overridden to get another return
value.
Of course, it would be possible to automatically define a missing +=
using a defined +, and a missing + using a defined +=. Whether this would
be a good idea is certainly debatable.

That would lead into the directon of C++ where you can overload all these
operators independently. Dunno whether that is a good idea or not, but I
always liked the idea that there was only one set of operators I needed to
define and got the rest for free. We could maintain this by defining #+,
#- etc. in Object using #+= etc. internally. So we would retain the
automatism while not reducing freedom too much.

Any other thoughts on this?

Kind regards

robert
 
R

Robert Klemme

Dennis Ranke said:
I didn't overlook that one. It was in the first post already:

<quote>
On the downside:
[...]
- dup might or might not be the appropriate method to create a copy
depending on the class at hand. When defining operator +, we are
currently
free in deciding what's the type of result of this operation. That
freedom would definitely go away.
</quote>

A solution would be to provide another method (maybe #copy or #op_dup or
similar) that defaults to #dup but can be overridden to get another
return
value.

Ok, but I can't really see this working. Let's take a Vector class, for
example, that defines the following operations:

Vector = Vector + Vector (adding two vectors)
Float = Vector * Vector (calculating the dot product)
Vector = Vector * Float (scaling a vector)

Now the resulting type depends on both the actual Op used as well as the
type of the second operand. While it would certainly be possible to call
#op_dup with all of this information, it sounds quite ulgy to have one
method that has to know about the inner workings of all the operators.

Yep, true. I didn't think of multiple dispatch but you're right that this
is ugly. One could solve that by defining #op_dup this way:

def op_dup(op_sym, op_obj)
dup
end

That would allow overriding implementations to decide which return value
to create.
Another problem (which is much harder to solve) is: What kind of object
should receive the *= message in the case of the "Float = Vector * Vector"
operation?

Clearly the result of #op_dup. First a copy is created and then the
inplace operation is performed.
A Vector that turns into a Float while processing the message?
I can't say that I like the sound of objects changing their class in this
way.

No instance changes its class here.
I think the closest you could ever get to this would be the current ruby
way (ie. defining #+= using #+) and optionally being able to define your
own #+= as well.

.... or the other way round, i.e. defining #+ using #+= and optionally
defining #+ as well.

Thanks for the feedback!

Regards

robert
 
R

Robert Klemme

Dennis Ranke said:
Well, yes, but what kind of object should #op_dup return? It would have to
be a Float, because that's the result of the operation, but upon receiving
the first #*= message it would have to multiply two Vectors, while for
later #*='s it would have to act like an ordinary Float.
Possible, but not exactly what I would like to do.

Especially since a Float typically does not have a method for multiplying
it with a Vector. Does #coerce help here?
True, of course.
There is a disadvantage to both, though: It would be ugly to inhirit from
such a class and trying to change the behaviour of #+ and #+=, as you can
never be sure that it is enough to just overwrite one method, so you'll
always need to overwrite both.

Yeah, exactly. I think, that's why I like the approach it is today.

Still not really satisfying. I see the disadvantages of both sides
clearer now, but I don't see a better solution right now. :-(

Maybe we'll see multiple dispatch in Ruby 3 and then these things get
easier. :)

Again, thanks for sorting that out!

Regards

robert
 

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

No members online now.

Forum statistics

Threads
474,143
Messages
2,570,822
Members
47,368
Latest member
michaelsmithh

Latest Threads

Top