I miss function overloading. I can see why they left it out of Ruby: overloading based on static types is, of course, right out, which only leaves us overloading based on the number of arguments. And even that has a bit of a staticish feel to it, and, what with Ruby’s nice varargs handling (nothing revolutionary, obviously, any Lisp programmer will be familiar with it), isn’t necessary. Besides, you can just call your different versions of the functions by different names, can’t you?
Well, you can, but that doesn’t necessarily make your code any clearer. Which is, for me, a largely theoretical objection right now; a more practical one at the moment is: no, you can’t always. I wrote my first two functions that overloaded themselves based on the number of arguments yesterday (using varargs plus a conditional on the size of the extra arguments array). One of those functions was
initialize, which is magic. I won’t argue in favor of having zillions of constructors, but I don’t think it’s unusual to want to have a couple of different constructors for a class. There are, of course, ways around this – static factory functions are another obvious choice, and are quite appropriate in many circumstances – but I sometimes like just defining a few constructors.
And the other function that I overloaded based on the number of arguments was the array operator,
. (Plus the array assignment operator,
=.) Again, I could simply have renamed one of my overloads – I wouldn’t blame my readers for wondering what I have in mind that I might want to sometimes use one index to reference and sometimes two – but I don’t think that doing so would have made uses any clearer.
Which brings me to the other use of the term overloading, namely operator overloading. So far, I really like what Ruby does here. It doesn’t go as far as C++ does: it doesn’t let you overload absolutely everything.
||, is one example, but more interesting is
!=. After all,
a != b should always be the same thing as
!(a == b), so why not simply enforce that? They don’t enforce all such restrictions – I believe that you can overload
> separately, for example (though, if you’re defining those, you almost always want to just define
include Comparable), but they made a sensible choice as to the restrictions to enforce.
Even more fun is the way they handle assignment: you can overload
a.foo = bar by defining a
foo= operator on
a‘s class. This is great – at one stroke, it means that you no longer have to feel guilty about exposing member variables. To be sure, the language doesn’t let you expose them directly – they’re always private – but, if you have a member variable
@foo that you want to expose, you can do
attr_accessor :foo. (You can also use
attr_writer if you want to expose it read- or write-only.) This defines
foo= methods that get and set your member variable; if you later decide that you want non-trivial read and write methods (perhaps removing the actual member variable entirely), you can replace that with appropriate definitions, and your users will be none the wiser. All of a sudden, a certain ease of implementation versus cleanliness / ease of extention tradeoff vanishes: you type as little as you need to at the time, and don’t have to worry about that posing problems down the road.
Off to write some more code…
There are no revisions for this post.