13 Jan 2006

Operator Overloading in Ruby

Update: Looks like I was wrong. See the second last comment.

The best implementation of operator overloading I've seen (next to Lisp's ;) is Ruby's - you just define methods named ==, <, etc. The C++ mechanism is really ugly - they just go for more syntax, like the operator keyword. Python's is better, but still ugly - you define methods named __eq__, etc.

The beauty of Ruby's system is that there is very little language support for it. In Ruby,
  • Characters like ==, = and ? are allowed in method names and other identifiers
  • Methods can be called without () at the end; simply give the arguments after the method name
So you can do this:
if dog.hungry?
dog.eat biscuit
end
Since + is a valid method name (and numbers are objects), you can say:
puts 3.+7

The only support Ruby gives for operator overloading is to allow the dot to be removed. The rest of it follows automatically from the power and flexibility that's already there.

I find a model that says, "the operator + maps to the method +" to be more straightforward than one that says, "the operator + maps to the method __add__". So + and .+ is just the operator/method duality - when you look at it as an operator, it's +, and when you look at it as a method, it's .+ .

Incidentally, the very same power simplifies properties (things that look like instance variables but are backed by methods) - a read of a property dog.age is just a call to the method age with the parentheses removed, and a write is a call to the method age=. How elegant!

With a no-distinguished-object style (where the self argument is syntactically treated similar to the other arguments), unless you have prefix syntax, mapping a+b to +(a, b) is little more of a syntax transformation than mapping it to a.+(b). But there's a transformation going on in both cases, so I suppose there isn't much difference. And I would still prefer overloading + by definining a function named + rather than __add__ (or another name).

Last Modified: 13 Jun 2006

4 comments:

  1. The parameter thing will go away though:

    # test.rb
    foo = ["blah blah"]
    foo.push "foo"

    ~ $ ./test.rb
    test.rb:2: warning: parenthesize argument(s) for future version

    ReplyDelete
  2. I'm not sure future versions of Ruby will require parameters to be enclosed in parenthesis. The warning in the output you gave seems to be there only in the 1.9 development release (at least it's not there in 1.8.2 stable) and I believe 1.9's syntax changes are there only so that people can experiment with them. I haven't found any source that says the change is confirmed. If you have, could you please share it?

    ReplyDelete
  3. Anonymous6:48 am

    It would be pretty neat if all Ruby did was nuke the dot for us, but actually there's a little bit more magic than that.

    For example, try 2*3+4 versus 2.*3.+4 -- the former is 10, and the latter is 14 (so the "+" and "*" methods get subjected to order-of-operations before being called, at least).

    Interestingly, in Smalltalk (which Ruby inherits much from) I do believe this is not the case: arithmetic is purely left-to-right. More elegant? I dunno -- depends on which side of the abstraction you are, I guess.

    ReplyDelete
  4. Anonymous8:40 am

    |.X"Operator Overloading in miss ruby;]X-unlawful move,ieie:)/=*.|

    ReplyDelete