One excellent feature of the CLR in general and C# in particular is deterministic finalization: the ability to control when resources are released. In C# this is realized with the
IDisposable interface and the
using keyword. Effectively, the statements:
Are equivalent to the following
The usual use of the
Dispose method which is, as you can see above, called when
myFile goes out of scope is to perform general clean up. In this case it would release any open handles to the file, if any: doing so in this deterministic manner is in most situations a much better idea than waiting until the garbage collector gets around to finalizing your object and doing the cleanup there, although
File will of course support this paradigm to ensure that its open handles are eventually released.
There are actually more interesting uses than resource management that are made of the using keyword in C#, such as transaction management (the scope of the transaction is the scope of the using block), mock playback (mocks are automatically verified by Dispose) and many more of the class of scope management problems encountered during everyday programming (e.g. calling
EndUpdate on a particular
Control while you do lots of setup work on it). Hopefully this litany of examples has convinced you that we simply must have “using” added to Java - but alas that particular behemoth is unlikely to acquire this rather nice feature any time soon. Instead I have elected to show you how we can add this feature to a JVM based language, Scala, using the advanced features of its type system and functional programming paradigm.
Right, let’s get started! First, let’s have an interface for all things we want to consider
Disposable, just as in C#:
Don’t let the word
trait throw you: although you can do some cool stuff with traits, for our purposes they are just like Java’s interfaces. We have simply defined a method, dispose, on the
Disposable interface that will be called to do cleanup.
Now, we could just have all things we use
using with implement this interface, but this feels kind of limiting. This is all well and good for our own classes, but what about interfacing with Java code that doesn’t know a thing about our interface? In particular, it would be nice to be able to use our new functionality with JDBC and SWT, which have a common pattern of making use of close and dispose methods respectively to perform cleanup. So, let’s define two implicit conversions, known as “views” in Scala-land, from objects that define methods with those signatures to a
This is cool stuff. These function is making use of a very nice feature of Scala’s type system, structural subtyping, to operate on arbitrary objects that define particular methods. For example, the type
defines a type of class
S which must contain the method close, which takes no arguments and returns
Unit, in Scala). With this, it is statically valid to call
close() on any variable of type
S within the body of our implicit conversion, and I make use of this by returning a new anonymous inner class which implements Disposable and calls the close method of the implicitly converted variable when dispose is invoked (wrapping it in appropriate exception handling, since e.g. the “close” method in JDBC can throw SQLException, and we don’t want that masking exceptions thrown in the body of the using block). We define the view on things with a dispose method similarly, but this time we needn’t handle exceptions since we assume anything with a dispose method is already going to be well behaved.
Notice that this is effectively statically safe duck typing, so you can have all the flexibility of a dynamic language like Python in exactly the places you need it, but keep the compile time guarantees of Scala in the majority of your code base, where you don’t need that power.
Now all that remains to be done is to define the using “keyword” itself. There isn’t a way to perfectly emulate the C# syntax in Scala, to the best of my knowledge, but we can come pretty damn close by defining a function which takes one curried argument which is the thing to dispose and another which is the block to dispose it after:
To those uninitiated in the ways of Scala, that first line can look a little scary, so let’s look at it in more detail.
tells us that we are declaring a method called
using which is parameterized on two type arguments:
S, which must have a view of type
says that the method takes two arguments:
what of type
S (which we specified must be
Disposable in the type constraint earlier) and a block which takes one of those
S objects and returns a
T. The fact that brackets, rather than a comma, separate the two arguments shows that the method is curried, i.e. it may be partially applied by its callers: this makes for some nicer code later on. The final
T just tells us that the method returns something of type
Armed with this understanding, it should be very easy to see that the body of the function is statically safe and does just the same thing as the C# example I showed at the start of this post. That’s all pretty neat! Let’s see some of the code you might write using this function, assuming that the method definitions I’ve just given have been added to an object called
Disposable which accompanies the trait of the same name:
This will print:
Constructed Doing something Closing
Lovely stuff! The sharp eyed reader will have noticed that the implicit conversions I defined above actually both apply for some objects (i.e. those which have both
dispose methods): they might then be tempted to ask which conversion to
Disposable gets used by the compiler for such an object. The answer for me is that it seems that the close method is always picked as the one to be called by
Disposable.dispose, but I suspect this is an implementation detail, and will probably even vary from version to version: from the compilers perspective these two choices must be equally good.
I hope this article has gone some way to showing you some of the advanced features of Scala and how they can be used to solve real problems in software engineering. Note, however, that if you are going to play along at home you need at least version 2.6 of the language, as structural types were only added recently