Preamble: In order to understand this post you should know a little bit (a little is enough, that is how much I know) about ExpandoMetaClass and Categories in Groovy.

DSLs that involve existing classes might be a source of long term sorrow. Let me give an example: Imagine that you want to make a small DSL to handle equations, like

x = new Symbol("x")
(2 * x).differentiate(x) //Result is 2

The problem is that the * operator of Numbers doesn’t know how to handle Symbols, therefore an exception would be raised. The obvious solutions as discussed before on mailing lists and blog posts are:

Categories

Categories would solve the problem, but at the expense of polluting the source with things like

use (Something.Category) {
  //code here
}

Not a disaster, but not pretty too…

Talking about disasters…

Expando over Numbers

The idea here would be to change the behavior of Numbers to be able to handle Symbols. Code would be very clean, no need for uses…

As somebody said on the groovy mailing list: This is disaster in the making. The problem is that I change Numbers, then, for another valid reason you change Numbers, somebody else also changes Numbers… This is chaos. Or at least it would make code from different sources potentially not inter operable or exhibiting very strange, buggy, behavior. This is clearly akin to the “global variable” problem. I believe that in the long term and with big software projects, this approach is a dead end.

Enter Python

Python actually has a workaround (I will not call it a clear, beautiful solution) that might be somewhat useful here. Imagine that you do

1 + x

The default 1 (default class for number) is not able to handle the symbol. For python that is OK, it will try to call a “right add” method of x (Search for __radd__ in this page). So, the default behavior is not to raise an exception if the left object cannot handle the operator, but to try to call the “right” version on the right object (if it fails then raise).

Not perfect, but might be just enough to avoid Expando in anger.

I do believe that people still don’t appreciate the consequences of Expanding core classes and the interop disaster that that can entail.

Social network sharing
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • DZone
  • Reddit
  • Slashdot
  • StumbleUpon
  • Technorati
  • LinkedIn
  • connotea
  • FriendFeed
  • Twitter
  • Yahoo! Bookmarks

4 Comments

  1. Robert Fischer says:

    Why is this radd thing better than hacking Number directly? If someone goes in and hacks Number (say, defaulting to String concatenation), you’re still just as likely to be screwed. I don’t see what you’re gaining, and it seems like it’s making it harder to figure out what the code is doing.

  2. tiago says:

    Robert,

    I actually agree with both your points:

    1. I do think there is a problem with anybody being able to change core classes is very large applications using unknown external code (at least there should be a mechanism to force core classes to behave in a default manner).

    2. As I said in the post, I don’t think the solution is perfect, it is just an avenue that can be explored.

    At the end of the day I think the problem comes from the mixing of specification and semantics. But that will be the topic of another post.

  3. Marc says:

    I don’t see what the big deal is with changing the core classes. If you have the requisite unit tests, the risks should be minimal. You can also make a rule on the project that nobody messes with core classes until they discuss it with the team first.

    I think this is a solution looking for a problem.

  4. tiago says:

    Marc,

    Until you use a foreign library that also messes with core libraries…
    If you control all the code, then its OK. But if this design pattern becomes common and you use foreign libraries that do this…

Leave a Reply