Help overloading operators

I’m trying to overload Float#+, but I’m doing it wrong and it’s breaking
Float#+. For example:

require ‘complex’
=> true
2 + Complex(1,1) # → works fine
=> Complex(3, 1)
require ‘poly’ # → breaks Float#+
=> true
2 + Complex(1,1) # → broken
NoMethodError: undefined method plus' for #<Complex:0x1aa2d0> from ./poly.rb:216:in plus’
from ./poly.rb:216:in `+’
from (irb):4

This is the code I’m using:

class Float
alias plus +
def +(other)
other.kind_of?(poly) ? other.+(self) : self.plus(other)
end
end

I don’t understand the error. I defnined “plus” for Float, not for
Complex. Does anyone know how I should be overloading this operator?

Thanks a lot.

···

from :0


Daniel Carrera
Graduate Teaching Assistant. Math Dept.
University of Maryland. (301) 405-5137

Hi,

This is the code I’m using:

class Float
alias plus +
def +(other)
other.kind_of?(poly) ? other.+(self) : self.plus(other)
end
end

I don’t understand the error. I defnined “plus” for Float, not for
Complex. Does anyone know how I should be overloading this operator?

I can’t reproduce your error.

$ ruby -rcomplex -rpoly -e ‘p 2+Complex(1,1)’
Complex(3, 1)
$ ruby -rcomplex -rpoly -e ‘p 2.0+Complex(1,1)’
./poly.rb:4:in +': undefined local variable or method poly’ for 2.0:Float (NameError)
from -e:1

Apparently, something wrong is in the remain of your code.

···

At Sat, 8 Feb 2003 09:55:23 +0900, Daniel Carrera wrote:


Nobu Nakada

Well, it’s 3am and I can’t sleep, so if this makes no sense, just try to be
understanding, ok?

:slight_smile:

I’m trying to overload Float#+, but I’m doing it wrong and it’s breaking
Float#+. For example:

require ‘complex’
=> true
2 + Complex(1,1) # → works fine
=> Complex(3, 1)
require ‘poly’ # → breaks Float#+
=> true
2 + Complex(1,1) # → broken
NoMethodError: undefined method plus' for #<Complex:0x1aa2d0> from ./poly.rb:216:in plus’
from ./poly.rb:216:in `+’
from (irb):4

This is the code I’m using:

class Float
alias plus +
def +(other)
other.kind_of?(poly) ? other.+(self) : self.plus(other)
end
end

I don’t understand the error. I defnined “plus” for Float, not for
Complex. Does anyone know how I should be overloading this operator?

···

----- Original Message -----
from :0

Is the Float code you posted the code on line 216 of poly.rb? Also, 2 is
not a Float, of course! Did you do that same sort of thing for Fixnum and
Bignum? If you want, you can mail me your code and I’ll take a look at it.
(I had to solve a few problems like this when I was working or that symbolic
math stuff I sent you earlier, so it’s all fresh in my mind.)

Chris

I’m still having trouble overloading operators. Here is a minimal example
that produces the error. Perhaps someone can discern the problem.

$ cat test.rb
class Poly
end
class Fixnum
alias plus +
def +(other)
other.kind_of?(Poly) ? other.+(self) : self.plus(other)
end
end
$ irb

require ‘poly’
=> true
require ‘complex’
=> true
2 + Complex(1,1)
NoMethodError: undefined method plus' for #<Complex:0x1a9d18> from ./poly.rb:122:in plus’
from ./poly.rb:122:in `+’
from (irb):3

···

from :0


Daniel Carrera
Graduate Teaching Assistant. Math Dept.
University of Maryland. (301) 405-5137

Small typo in my last email. I wrote “require ‘poly’” instead of
“require ‘test’”. But the error is definitelly here. Here it is again
(correctly this time):

$ cat test.rb
class Poly
end
class Fixnum
alias plus +
def +(other)
other.kind_of?(Poly) ? other.+(self) : self.plus(other)
end
end
$ irb

require ‘test’
=> true
require ‘complex’
=> true
2 + Complex(1,1)
NoMethodError: undefined method plus' for #<Complex:0xe62c0> from ./test.rb:6:in plus’
from ./test.rb:6:in `+’
from (irb):3

···


Daniel Carrera
Graduate Teaching Assistant. Math Dept.
University of Maryland. (301) 405-5137

Is the Float code you posted the code on line 216 of poly.rb? Also, 2 is
not a Float, of course! Did you do that same sort of thing for Fixnum and
Bignum?

Yeah. Line 216 is actually the Fixnum one.

If you want, you can mail me your code and I’ll take a look at it.

That’d be great. I don’t want to bombard the list with attachments, but
if anyone is willing to look at what I’ve done, you can download it at:

http://www.math.umd.edu/~dcarrera/ruby/math/

The relevant files are “poly.rb” and “poly/arithmetic.rb”.

Thanks for your help.

···

On Sat, Feb 08, 2003 at 08:17:17PM +0900, Chris Pine wrote:


Daniel Carrera
Graduate Teaching Assistant. Math Dept.
University of Maryland. (301) 405-5137

Figured out your problem. Consider the following code (which crashes):

require ‘complex’

class Fixnum
alias plus +

def + other
  plus other
end

end

p 2 + Complex(1,1)

./test.rb:11:in plus': undefined methodplus’ for #Complex:0x276c7b8
(NoMethodError)
from ./test.rb:21:in `+'
from ./test.rb:25

The problem is from the Complex#coerce code:

def coerce(other)
if Complex.generic?(other)
return Complex.new(other), self
else
super
end
end

No big deal. Complex reverses the order in the coerce (which it has to do,
since Fixnum has no idea how to add Complexes), which means the method ends
up getting sent to your Complex object.

Solution? Well, it looks like the right thing to do (and I did not know
this, so ignore all of that other code I sent to you) is to NOT overload
Fixnum’s arithmetic operators. Instead, do a coerce yourself.

So here’s my question: Why dear god why is Complex#coerce not on the
stack?? I had to send in my expectation object (see an early ML message
about it) to mimic the Complex object and report what methods were being
called (what was expected of it). Isn’t the call stack supposed to report
that sort of thing? I know this is after it went through the interpreter,
since it wasn’t Ruby code which called coerce, but still!

Chris

Thanks Chris. That solved the problem. Though I don’t really understand
what I just did. I sort of blindly copied the code from complex.rb.

[snip]

The problem is from the Complex#coerce code:

def coerce(other)
if Complex.generic?(other)
return Complex.new(other), self
else
super
end
end
[snip]

What does #coerce? do. What does that ‘super’ do? In particular, how
does that solve the addition problem?

Once again, thank you very much. I never would have figured that out.

···


Daniel Carrera
Graduate Teaching Assistant. Math Dept.
University of Maryland. (301) 405-5137

I wouldn’t have know it, either, had you not asked the original question.
:slight_smile:

···

----- Original Message -----

def coerce(other)
if Complex.generic?(other)
return Complex.new(other), self
else
super
end
end
[snip]

What does #coerce? do. What does that ‘super’ do? In particular, how
does that solve the addition problem?

I don’t know the general answers to these questions, but I think I
understand what is going on here.

When I say 2 + Complex(1,1)', Fixnum#+ is called. Note that this is never overridden, so this is the original Fixnum#+. What I am guessing is that Fixnum#+ first looks to see if you are adding another fixnum, or what. If it knows how to do the conversion so that a meaningful addition can be performed, then it does. If not, it calls other.coerce(self)‘. (I am
guessing all of this, so please someone correct me if I am wrong.) In this
case, it calls Complex#coerce. It is expecting to get an array of two
objects back of “the right type”, though it may not even know what that type
is. Then it sends the original message (in this case, +', in the previous case, plus’) to the first element in the array. In other words, if
coercing returns [a,b], then it calls `a.mesg(b)'.

So what does Complex#coerce do? Well, I think that `return’ is actually
returning an array. (I personally find this notation confusing and
obnoxious… what’s the point of this?) However, notice what happened: it
returned two Complexes!

I guess the idea behind coercing is that you can coerce either way. In
this case, even though it is fixnum addition being called, after coercing
2.+' gets an array of two complex numbers: first the complex version of itself (2), and then the original other complex number (Complex(1,1)). Then it gives up on itself (meaning the fixnum 2) and sends the object to it's coerced self. What's cool is that in general coerce’ could return two
Fixnums (coercing the other object into a Fixnum), or it could coerce the
Fixnum into something else (like we saw).

No weird overloading and aliasing… pretty damn slick.

I don’t know what super does… probably calls Numeric#coerce (if Complex
is a subclass of Numeric) or Kernel#coerce (which probably just generates an
exception in most (all?) cases). I think the idea was to handle what
Complex knew how to handle, and to pass everything else on. Even if it does
just generate an exception, at least it will be a familiar one.

Chris

Excellent explanation, Chris. I threw it on the Wiki:

http://www.rubygarden.org/ruby?CoerceExplanation

If more questions on “coerce” come up (I know I’ve been meaning to
ask), we’ll have some fodder for the FAQ.

Gavin

···

On Sunday, February 9, 2003, 1:15:32 PM, Chris wrote:

I don’t know the general answers to these questions, but I think I
understand what is going on here.
[…]