Thursday, March 26, 2009

ARM Blocks in Scala: Revisited

Over a year ago, when I was new to Scala, I tried implementing Automatic Resource Management. I was not pleased with the result, so I ended up recommending ManagedResource from Scalax. After I came across a couple of links to my original post, I decided to point out a different approach that I have seen which seems to work well and appeals to me, personally.

Next, I'll try to figure out where I went wrong originally, but those who are only interested in the new approach can skip the next section.

Unnecessary Complication
One of my original goals was to allow multiple resources per block. This ended up making the implementation much more complicated, especially with regard to exception handling. Also, in my first approach I included an exception handling mechanism which turns out to be unnecessary if we're only dealing with one resource at a time. The biggest problem with my first attempt, however, was that the resource had to be initialized outside of the ARM block, which, when multiple resources are initialized, can lead to the very resource leaks which we are trying to avoid. It was a good effort, however, if I do say so myself, and helped me to learn the language a bit.

A Simpler Approach
Lucky for me, Scala's first expert, Martin Odersky, also thought ARM was a good example of the flexibility of the language. I don't know when he started using ARM in his talks, but the first time I saw it was at JavaOne 2008, and then again at the Scala Lift Off event the following weekend. I will not re-post Odersky's implementation here without his permission (not that I think he would object), but I recommend that you take a look at slide 21 of his FOSDEM 2009 presentation (which was hopefully posted with his permission).

Odersky's approach is much more elegant. It only allows a single resource to be managed per block, which is actually all you need since ARM blocks can be nested to support multiple resources. Exceptions are not caught so that the caller can handle them properly. Simplicity. This ends up being very similar to the approach that C# has taken, so he chose to name the method the same as the C# "using" keyword.

Here's how you use it:


//print a file, line by line
using(new BufferedReader(new FileReader("test.txt"))) {
reader => {
println(reader.readLine)
}
}

//copy a file, line by line
using(new BufferedReader(new FileReader("test.txt"))) {
reader => {
using(new BufferedWriter(new FileWriter("test_copy.txt"))) {
writer => {
var line = reader.readLine
while (line != null) {
writer.write(line)
writer.newLine
line = reader.readLine
}
}
}
}
}


I actually like this approach better than the ManagedResource from Scalax because the syntax feels more natural; it feels like you are using a language feature. I always thought ManagedResource was a bit strange because it felt like an abuse of the for-comprehension, but that's just my opinion.

Since I hadn't seen anyone point out Martin Odersky's "using" implementation, I thought I would post about it. I think ARM is great and the fact that it can be implemented as a library in Scala is phenomenal.

If we're lucky, Java SE 7 will have ARM thanks to Project Coin, but it's getting more and more obvious these days that Java is not progressing as a language. Now if only I could do my everyday coding in Scala....

No comments:

Post a Comment

Post a Comment