March 20, 2007

Whoops! Open classes not cool after all!

Ruby, Perl and many other languages let you reopen an existing class or namespace and add additional methods to it, or replace existing methods with new code. Everyone thinks that is "like super-cool" and "really feels the spirit of getting things done", probably accompanied by some generic "LOL Java, agile languages FTW!" rant.

Surprisingly, open classes have one serious problem: People are using them!

Right now the two most popular uses for reopening classes are (1) adding methods to core classes like objects or lists and (2) patching totally not public code with your own funky version. Both are terrifyingly bad ideas which will probably break with the next update of the code you're patching, or as soon as another clever library attempts to do the same. But let's enjoy some examples together:

  1. It is impossible to use prototype.js and json.js at the same time because prototype.js does something nasty to Object.prototype. But never mind since these are only the two most commonly Javascript libraries on the planet.

  2. What Rails is doing to the Ruby core, and what some plugins are doing to Rails, is downright scary in some places. Thankfully Rails is heavily maintained and bugs are patched quickly.

  3. While developing a World of Warcraft addon in Lua, we discovered that the preferred way to intercept chat messages was to replace the existing message handler, intercept the messages you need and hand over the rest to a copy of the old handler. Unfortunately, since our addon name was lexically smaller than the name of another addon that used the same trick, and World of Warcraft loads addons in alphabetical order, our new message handler was replaced by a third addon handler by that other, lesser addon and everything went to hell.

I'm not even sure if the last example is valid in this context, but it's also about bad scope and globals, and I really needed to stress the fact that I'm writing kick-ass World of Warcraft addons this very minute.

Anyway, don't you think it's funny how twenty years after people realized that "globals are teh suck" we're falling into the same bad habits all over again? Yes, open classes are fun toys to play with, but you can also fuck up royally if you don't pay attention. So, please, do think about what happens when other libraries attempt to pull the same hackery as you. Do consider the chance that a second library also wants to decorate the draw method, and do assume it will also save the existing method using alias :original_draw :draw. And OH MY GOD do not patch up private methods that have clearly not been cleared for public consumption by the original author!

Patching private methods. I mean, what the hell. Think of the children!

Comments

Whoa dude, you’re alive.

Actually, Perl thankfully doesn’t suffer too much from this; not nearly as much as Ruby, anyway. It’s probably because the language isn’t OO from top to bottom so you can’t do some of the unspeakable things Rubyistas are doing.

And I do think it’s necessary that a language allow re-opening classes… but good grief, it just shouldn’t be used in regular code! The facility is there to permit metaprogramming and possibly for when you’re on a deadline and have to deal with a brokenly designed library, in which case it gives you a bad coping strategy rather than no coping strategy.

But it boggles my mind that the problems with wanton exploitation of open classes aren’t blindingly, searingly obvious. I mean, Rake stuffs a kitchensink of filesystem methods into Object, which breaks Net::SFTP’s reliance on method_missing to delegate methods to the right backend module, which leads Assaf Arkin to conclude that method_missing should be avoided. What the …?

People need to find ways to get private method semantics out of their favourite language, and use them.

Posted by Aristotle Pagaltzis (#)

Agreed on open classes saving the deadline.

Rest of the comment distilled into another entry.

Posted by Henning Koch (#)

Python allows this too but with some restrictions. 1) Types written in C (which includes core types) cannot be amended. If you want something almost like a bultin subclass it and use that.
2) Types written in python can be manipulated at runtime. Doing this is called "monkey patching" it is frowned on (for the reasons you describe). Just subtype the thing if you need different behavior.

Posted by Jack Diederich (#)

Post a comment

Thanks for signing in, . Now you can comment. (sign out)

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)


Remember me?