Operators +, += and = in Ruby 2


(Robert) #1

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

(Simon Strandgaard) #2

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

ruby a.rb
0
1
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

···

On Wed, 25 Feb 2004 16:12:16 +0100, Robert Klemme wrote:


Simon Strandgaard


(Austin Ziegler) #3

Better to have a new method for this:

x.add(y)

The problem is that assignment works on variables, but + is a method call
on an object. Ruby is the only language that I know that has such a
distinction, but it’s one that (to me) makes perfect sense.

-austin

···

On Thu, 26 Feb 2004 00:14:50 +0900, Robert Klemme wrote:

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.


austin ziegler * austin@halostatue.ca * Toronto, ON, Canada
software designer * pragmatic programmer * 2004.02.25
* 10.55.34


(David A. Black) #4

Hi –

···

On Thu, 26 Feb 2004, Robert Klemme wrote:

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.

But wouldn’t calling dup be slower than not calling dup?

x,y = “a”, "b"
z = x + y

vs.

z = x.dup + y

David


David A. Black
dblack@wobblini.net


(Dennis Ranke) #5

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

So in some cases it is not possible to correctly define a *= operator,
while a * operator is still desirable.

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.

···

On Wed, 25 Feb 2004 16:12:16 +0100, Robert Klemme bob.news@gmx.net wrote:

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?


exoticorn/farbrausch


(Austin Ziegler) #6

I don’t see the problem here.

p A.count # => 0
a = A.new
p A.count # => 1
a += 5
p A.count # => 2
a.close
p A.count # => 1

It’s doing exactly what you told it to do here. I’m presuming that
you want to do:

def +=(value)
@value += value
end

Ultimately, what you’re doing with + in this case is trying to do a
#become operation, which Matz has – at least as far as I can see –
said he won’t support.

-austin

···

On Thu, 26 Feb 2004 00:29:46 +0900, Simon Strandgaard wrote:

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.

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


austin ziegler * austin@halostatue.ca * Toronto, ON, Canada
software designer * pragmatic programmer * 2004.02.25
* 11.00.12


(Robert) #7

“Simon Strandgaard” neoneye@adslhome.dk schrieb im Newsbeitrag
news:pan.2004.02.25.15.23.19.681692@adslhome.dk

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

ruby a.rb
0
1
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


Simon Strandgaard

Kind regards

robert
···

On Wed, 25 Feb 2004 16:12:16 +0100, Robert Klemme wrote:


(Robert) #8

“Dennis Ranke” dennis.ranke@epost.de schrieb im Newsbeitrag
news:opr3y1zulz4wwk8i@News.CIS.DFN.DE

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:

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.

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

On Wed, 25 Feb 2004 16:12:16 +0100, Robert Klemme bob.news@gmx.net wrote:


(Dennis Ranke) #9

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

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.

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.

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

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.

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.

···

On Thu, 26 Feb 2004 12:24:13 +0100, Robert Klemme bob.news@gmx.net wrote:


exoticorn/farbrausch


(Robert) #10

“Dennis Ranke” dennis.ranke@epost.de schrieb im Newsbeitrag
news:opr3y7ryzv4wwk8i@News.CIS.DFN.DE

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

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.

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.

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

···

On Thu, 26 Feb 2004 12:24:13 +0100, Robert Klemme bob.news@gmx.net wrote:

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.

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

(Dennis Ranke) #11

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.

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.

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.

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.

···

On Thu, 26 Feb 2004 13:30:52 +0100, Robert Klemme bob.news@gmx.net wrote:


exoticorn/farbrausch


(Robert) #12

“Dennis Ranke” dennis.ranke@epost.de schrieb im Newsbeitrag
news:opr3y90tet4wwk8i@News.CIS.DFN.DE

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.

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?

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.

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. :frowning:

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

Again, thanks for sorting that out!

Regards

robert
···

On Thu, 26 Feb 2004 13:30:52 +0100, Robert Klemme bob.news@gmx.net wrote: