lördag, oktober 06, 2007

Operator Overloading in Ruby

I really like the fact that Ruby has operator overloading. In many cases this feature is not very fitting in a language, but in Ruby it definitely has its place. So what's the difference? Why do people generally say that operator overloading in C++ is a bad thing, and that Java shouldn't have it? I believe the main reason is static typing. When you have static typing and combine that with a verbose language, operator overloading doesn't really fit in. You can't create DSL looking API's out of the language since the line noise usually make such efforts futile. And then you're left with only the bad parts of operator overloading.

In Ruby on the other hand, it fits very well. It actually makes sense to be able to redefine and add operators since there are many places where your code can be made much more readable with it. Of course, you always have to be careful and not go over board with it, but when used riht it improves readability to a large degree. A typical example of nice use of overloading is in Hpricot:
doc/:html/:body/:p/:img
Here the division operator have been overloaded to make it possible for you to write XPath like search strings beginning on a parsed Hpricot document.

Another place I really like is in the Ruby core library where the call method is usually overloaded as []. This makes sense if you think about it - in most cases the objects you can call on will take parameters and return a result based on the parameter. This can be mapped quite neatly to an Array or Hash containing values that are returned, and in fact you can imagine swapping out this restricted usage of an Array or Hash to something that you can call things on.
If the [] method is all you used for access, duck typing makes it easy to send in a Proc instead of an Array or Hash.

Another reason using [] can be nice, is since Ruby doesn't let you override (). If you want to emulate that kind of behavior, [] can act exactly the same if you want it to.

In the Ruby core, the more useful things that implement [] is Array and Hash, of course, UnboundMethod, BoundMethod, Proc and Continuation. This means that you can do something like proc{ puts "hello" }[] if you feel like it.

There are lots of other nice operators to overload in Ruby, though. Some of the better ones are unary + and unary -, all the common arithmetic operators, ||, &&, |, &, <, >, <=, >=, ~, =~, **, ==, === and a few others. The two things missing here is the ! and friends, and (). But of course, adding () wouldn't really work with the Ruby language. ! on the other hand should be overridable.

So, you should use operator overloading in Ruby, but be careful that it makes sense and actually gives you a good abstraction, not just something "cool".

9 kommentarer:

Anonym sa...

I agree that operator overloading in Ruby is generally a good thing. However, I think that your contention that the unary not operator (!) should be overridable is a bad thing. Ruby considers all objects except nil and false to be true for the purpose of conditionals. Allowing an object to decide that it is false (if !obj ...) but not true would invalidate all sorts of otherwise reasonable logic about objects. The only case for overloading not I can think of is making RSpec a tad more readable.

I rather like the way Scala handles operators; namely that they're just method calls with a precedence determined by the first and last character. Such a view fits well with everything being an object, and Scala's type-system allows them to compile very efficiently in the "non-overloaded" case as well.

Konrad sa...

As far as I know, C# offers operator overloading with quite a success despite its static typing.

Anonym sa...

The && and || operators are not overloadable, mainly because they provide "short circuit" evaluation that cannot be reproduced with pure method calls.

Anonym sa...

I'm glad Ruby allows to overwrite the == operator, in Java I'm always in danger to write s == "bla" when I
mean equals.

Python allows you to overwrite the () operator, so that in every place where a function is expected, you can also provide a class with the __call__ method, which can be used to track state.

Unknown sa...

It would be nice if Ruby in new versions supported the following behaviour:

when some object is used as boolean expression (e.g. "if foo then"), call to_b (or to_boolean) on it. This would allow creating special proxy objects that could represent e.g. false expression (for now Ruby considers all non-nil-or-false objects to be true). Default implementation for Object#to_b would be 'true', False and Nil classes - 'false'. That would maintain backwards compatibility.

The other thing, it would be nice if foo( ... ) would transform into variable-or-method lookup and then call to value.call( ... ). I don't see a reason why this should be hardcoded.

Anonym sa...

cymbalta
augmentin
clonidine
Coreg
biaxin
clindamycin
azithromycin
zyrtec

prozac
risperdal
paxil
mobic
synthroid
lasix
prevacid
ultracet
vicodin
verapamil

valtrex
zantac
adderall
dopamine
singulair
strattera
toprol
topamax
valtrex
amoxicillin
plavix
oxycodone
inderal
nexium
neurontin
skateboarding shoes
work boot
wedge sandal
adidas shoes
rack room shoes
keen shoes

Anonym sa...

casino niagara
pechanga casino
barbary casino
casino morongo
wager casino
soaring eagle casino
wynn casino
rama casino
venetian casino
foxwoods casino

floral arrangement
flower bulb
flower seed
wild flower seed
funeral flower
flower grower
flower centerpiece
perennial flower
tulip flower
flower blossom
ftd flower

сitalopram
penicillin
anafranil
cephalexin
biaxin
diet fahrenheit pill
acne solution
adult acne
ayurvedic medicine
anti depressant


vitamin k
vitamin shoppe
vitamin e
vitamin b 12
vitamin b12
vitamin world
medicine cabinet
new england journal of medicine
occupational medicine
health care naming


pennsylvania lottery

GM sa...

The bit about static typing doesn't make sense to me as it stands. I'm guessing from the line noise bit you meant to say "explicit typing"?

Anonym sa...

Funny how you say operator overloading is not useful in statically typed languages, and then go on listing features (Xpath-like operators, function-collection equivalence) that Scala has out of the box.

Scala has some powerful DSL creation abilities, some of which would not be possible without static typing (i.e. user-defined control expressions) in a C-like syntax.