Thursday 11 December 2008

Do we need destructors?

This post is for my honoured, old-time, old-skool hacker colleague Stefan Z. As we were discussing the new and (then) hot Java language, we couldn't accept that it didn't have destructors. I cite Stefan:

The constructor/destructor pair is an incredibly powerful concept!
Well, you can't have everything: either we support garbage collection or destructors, isn't it? But it just one more point where Java sucks: the ugly try/finally block and then the explicit close() call like in the following code:

    BufferedReader reader =
new BufferedReader(new FileReader(aFileName));

try {
String line = null;
while ( (line = reader.readLine()) != null ) {
// process the line...
}
}
finally {
reader.close();
}
Ugly? You bet! And what I really don't like is that you cannot hide all the required handling in a library! In C++ you'd just write:

    ifstream f(aFileName);
string line;

while(f)
{
getline(f, line);
// process the line...
}
That's all, the plumbing is hidden in the destructor* and it is there automatically! It's the reason why Stefan called this concept an "incredibly powerful" one. But that powerful concept can cause problems in a multithreading and garbage-collected environment. As a matter of fact, a recent C++ standard proposal for the multithreading execution model opted for removing destructors from the language (!!!), or at least for not executing the static destructors in a multithreading setting! Of course, it's a shortcut in order to solve a rather complicated problem, but you get the idea, right?

So maybe the destructors are a little bit outdated, what do you think Stefan? All the more was I pleased as I recently stumbled on a Smalltalk pattern called "Execute Around Method" pattern** (to be true, I don't do any Smalltalk and have seen it in some Groovy example code). It's another possiblity to hide the plumbing in the library: you just define a static method doing all the dirty work and accepting your "payload" code - just like in an IP packet: we have the framing and the payload, and the user is only delivering the payload! Well, an example explains it best:
    def static use(closure)
{
def r = new Resource()
try
{
r.open()
closure(r)
}
finally
{
r.close()
}
}
The closure parameter stands for our "payload" code. This code is the hidden in the library. An application of this is the following Groovy code:

    new FileReader(aFileName).withReader
{ reader ->
reader.readLine(line)
// process the line...
}
// no need to close()!!!
We create a new reader, give it to the static withReader() library method, and provide a "code block" (as you'd call it in Perl) for execution. This code block (called closure in Groovy) gets as the parameter the ressource which will be closed at the end, just like the use() method shown above!

A destructor for the modern times! So the "incredibly powerful" idea can be saved?

---
* this is called a RAII-pattern in C++, see: http://www.hackcraft.net/raii/
** Kent Beck: "Smalltalk Best Practice Patterns", Prentice Hall, Englewood Cliffs, NJ, 1996.