Wednesday, June 3, 2009

Hey Scala, Finalize This

Years ago, when I moved from C++ to Java, I expected to miss a few things. My daily workhorses like templates & the STL were not available in the new environment. When it came to API design, which is really mini-language design, I could no longer rely on operator overloading. Even little efficiencies like inline functions were denied me.

But, it turns out, I didn't really long for any of those things as much as I anticipated. What I really missed, I mean what I felt no programmer could live without, was the deterministic destructor.

Bjarne Stroustrup champions RAISIN, Resource Acquisition IS INitialization. The idea is to represent acquired resources as class instances on the stack. So when those objects fall out of scope, a destructor fires and frees the associated resource.

A nice advantage of RAISIN is that it doesn't matter how you leave the scope. The code could simply return, or it could emit and exception. The burden falls on the class designer to remember to free up the resources. This is vastly superior to obliging every user of the class to remember to free the resources, in every place where it's used.

Note that we're not talking about manual memory management here. Resources include file handles and mutexes and database connections and whatnot. Acquisition occurs when the instance is initialized. Let's see an example.

//C++
void raisin_example(std::string name)
{
FileHandle const handle = FileHandle(name);

// use handle here, maybe some code throws
// an exception, but that's okay
//
}

All the cleanup work is done once and for all in FileHandle::~FileHandle(), and that always fires when the handle object falls out of scope. The simplicity and safety of he above contrasts sharply with Java.

// Java
void hardly_raisin(String name)
{
FileHandle handle = null;
try
{
handle = new FileHandle(name);

// use handle here, maybe some code throws
// an exception, but that's okay
//
}
finally
{
if (null != handle) handle.close();
}

Wow. That's a lot of scaffolding for something that every user has to remember to get right every single time. There's a lot of opportunity for things to go wrong here. We can't even make the handle const (or final), because it would then be out of scope of the finally clause. Java even gets worse if here are several resources, and we have to release them in the reverse order of which they were acquired.

Coming to Java required a profound shift in programming style. The lack of support for a good coding practice like RAISIN is one of the serious deficiencies of the language. I used to marvel at how much Java code I had written since leaving C+, as if hat was some kind of proof that RAISIN really wasn't so important. But now that I program in Scala, I shudder to think about how many lines of that Java code were just scaffolding.

Java's success despite this weakness says something about how important Java's strengths are. In other words, in the marketplace, garbage collection apparently trumps RAISIN. Thinking like a scientist, it's fun to speculate about what language features are more valuable than others, using trial by market as a grand laboratory.

The C# designers addressed this Java deficiency, after a fashion, by creating Disposable objects, and a convenient language syntax for cleaning them up.

// C#
void raisin_example(String name)
{
FileHandle handle = new FileHandle(name);
using(handle)
{

// use handle here, maybe some code throws
// an exception, but that's okay
//
}
}

That's not too shabby. All we have to do is oblige the FileHandle class to inherit from the IDisposable interface and implement a dispose method, which executes at the end of the using clause. The clean up code that the C++ programmer would have to put into a destructor goes into the dispose method instead.

Like Java, Scala also lacks deterministic destructors. However, Scala is powerful enough to emulate the C# "using" syntax. Let's make a zeroth cut at this in Scala. We'll define a Disposable trait, which we'll use in subsequent posts.

// Disposable.scala
package csharp

trait Disposable {
def dispose(): Unit
}

Finally, let's contrive a FileHandle class that extends csharp.Disposable. We'll use this in subsequent posts, too.

// FileHandle.scala
package raisin

class FileHandle(name: String) extends csharp.Disposable {

// constructor acquires resource here
//

override def dispose: Unit = {
// release resource here
//
}

// Nice things you can do with a FileHandle
//
def read(): Int = { /* details omitted */ }
def write(i: Int): Unit = { /* details omitted */ }
}

Last time, we showed how the Ruby unless modifier could be implemented in Scala. Next time, we'll take some steps towards bringing RAISIN to Scala.

No comments: