a re-examination of Ruby's reopenable classes
I used to be of the opinion that Ruby takes OOP to the limit, by allowing reopenable classes.
Take the factorial function. It operates on numbers, so conceivably, there should be a "factorial" message on receivers of type Number (to borrow from Smalltalk). Here's how it can be done:
class Fixnum
def factorial
return self if self <= 1
self * (self-1).factorial
end
end
Now, you can just do
5.factorial #120
But this approach doesn't lend itself to reuse - what happens if someone reopened and defined that method before you did? (Sounds like PHP's function_exists()
?) Paraphrasing the author of DSLs in Action:
if you do not put <method> in <class>, someone else will.
In fact, this is termed somewhat derogatorily as "monkey-patching".
Recently, I've been digging into Scala, and just like how I felt about Ruby when I first touched it, I feel that Scala brings OO design to a whole new level.
Re-examining the above factorial, in Scala this is done through "rich" wrapper classes and implicit conversions, which is a more reusable approach as compared to reopening classes.
The above factorial would then be implemented in Scala like so:
class MyInt(x: Int) {
def factorial(): Int =
if (x <= 1) x else x * new MyInt(x-1).factorial
}
The final step is the implicit conversion to tell Scala to treat Int's as MyInt's:
implicit def int2myint(x: Int) = new MyInt(x)
Now you can do
scala> 5.factorial
res5: Int = 120
I guess this isn't such a great example since we have to construct a new MyInt
instance on each recursive call to preserve the return type. But in cases where you need extra helpers/state info, you'll reap the benefits from having a "heavy", wrapper class.