Thursday, August 13, 2009

No Function Overloading? Not a Problem.

Recently, as I got back to Ruby, or should I say got back learning more about Ruby after skirting the language (in depth) and still getting Rails coding done... I decided I needed to kick it up a notch and get Rubier. I enjoy this task, because I can focus and think.

My source for this focus is The Well Grounded Rubyist and while the book has a lot of OO-newbish stuff I find the succinct portions readable and so I skim and enjoy the book. Anyway, one of the things that came up while reading it was the explanation of something that trips up most former C++ and Java programmers jumping into Ruby - no overloading (at least not the way that Java and C++ do it)!

Seems like heresy, for an OO language right? However, Ruby can realize the same concepts:
  • Ruby has default values for arguments so if you construct your parameter list correctly, you can realize overloading.
  • If you need optional arguments as part of the overloading option - Ruby has the optional argument support.
So you can get an overloading effect.

However, I want to point out something I have done even when programming in Java or C++: I have not not used overloading. I started realizing the value of this approach working with some Math whiz who was helping me discuss an API design. For him, overloading was a clever parlor trick that did nothing to tell the person reading your code what you mean by supporting n-ways to call the same logic - without extra documentation.

His suggestion? Name the overloaded functions to tell a story:
  • Overloaded: calc_net_pay(tax_rate, the_401k_rate) and calc_net_pay(tax_rate)
  • Informative: calc_net_pay_for_specific_401K_rate(tax_rate, the_401k_rate) and calc_net_pay_for_default_401K_rate(tax_rate)
Okay so it looks a little verbose, and when you read the declarations of the overloads the variables make things clear so why bother? Well, consider reading the invocations of your function. Now, with descriptive names you don't need more documentation, source code hot keys, or hover over pop-ups to explain what you are reading.

If you name methods explicitly and you encounter a function invocation like this:

calc_net_pay_for_specific_401K_percent(get_employee_tax_rate(), get_employee_401_rate())

You have a real good idea what the function is doing and also why it needs the data that you give it. In the overloading case its not just black box implementation - which is a good thing we call encapsulation; it is also black box semantics, which is not good in any case and requires yet more data to untangle the intentions.

Encapsulation should protect implementations, not obscure semantics. In programs, clear intentions are what reduce the occurrence of the Lava Flow Anti-pattern or inadvertent deletion by refactoring to "clean up the junk".

I have used overloading but in the end I think it is less OO than some assert, I also think it is a bit of a cheap trick. Most importantly, I know it can obscure semantics.

So for me, Ruby "not having overloading" (like Java or C++) is not a problem. In fact, what I just said about clarity of semantics also applies to Ruby's "overloading" by default arguments pattern. Maybe no public function should leverage that trick either. Perhaps it's just a tool to help keep the internals clean and lean?

No comments:

Post a Comment