Monday, 30 January 2012

Managing of opening and closing multiple resources automatically in Scala

Problem

Managing opening and closing multiple resource automatically. For example when you want to copy a file you need to open and close both input and output files.

Solution

I recently came across scala-arm library written by Josh Suereth. I liked the idea so much that I wanted to experiment with writing something similar, but a bit simpler myself.

Here is what I came up with:

import io.Source
import java.io.FileWriter
object ManagedDemo extends App{
import Managed._
import Managed.{managed => ®}
for (
fw <- ®(new FileWriter("target/test-out.txt"));
fr <- ®(Source.fromFile("build.sbt"))
){
fw.write(fr.mkString)
}
println("written:\n"+ ®(Source.fromFile("target/test-out.txt")).map(_.mkString))
}
trait Managed{
def close() : Unit
}
object Managed{
implicit def close2Managed[A <: { def close() : Any }](c:A) : Managed = Managed( c.close() )
implicit def dispose2Managed[A <: { def dispose() : Any }](c:A) : Managed = Managed( c.dispose() )
private def apply(closeResource : => Unit) : Managed = new Managed {
def close() : Unit = closeResource
}
def managed[A <% Managed](res: => A) = new Traversable[A]{
def foreach[U](f: (A) => U) {
val closable : A = res
try{
f(closable)
}finally{
closable.close()
}
}
}
}
view raw gistfile1.scala hosted with ❤ by GitHub

Explanation

The magic is in the foreach method and the way the for and map work. They both use the foreach method. We are using this fact and wrapping the iteration with try-finally block, so we can call the close or dispose method when the traversal is finished.

The rest of the magic are just implicit conversions between anything which contains def close() : Any or def dispose() : Any and Managed trait.

Enjoy!

No comments:

Post a Comment