Clean-up resource with Scala
Nov 17 15:48:39 <jinok> i hate wasting vertical space declaring vals that i need to initialize outside of a try block so i can clean them up in a finally block
Nov 17 15:48:53 <jinok> so i’ve started doing the initialization using lazy vals.
sealed trait Resource[+T] { val value: T def close: Unit def use[X](f: T => X) = try { f(value) } finally { close } } import java.io.InputStream object Resource { def resource[T](t: T, c: => Unit) = new Resource[T] { val value = t def close = c } implicit def InputStreamResource(t: InputStream) = resource(t, t.close) } object Example { import Resource._ def main(args: Array[String]) { val in = new java.io.FileInputStream("/etc/passwd") // print the first character of /etc/passwd println(in use (_.read.toChar)) } }
Reader’s exercise: Resource is a functor and monad. Implement the map and flatMap methods. Observe trickiness and ask why?
November 18th, 2008 at 1:03 am
Just starting to learn Scala (from C/Java, no functional background whatsoever) and I have no idea what this code is doing.
Well, that’s not true, I just have no idea HOW it is doing it
November 18th, 2008 at 10:35 pm
There are quite some posts about resource handling in Scala, good to see another one as it’s an interesting topic (preventing bugs). Nice use of implicits. How would your solution deal with several resources?
Peace
-stpehan
November 23rd, 2008 at 12:00 am
I’m not sure if the type signatures I have for map and flatMap are right — I’m still on the path to Monadic enlightenment.
I assume we want to use one resource to create another, cleaning up along the way.
The trickiness seems to be converting from B to Resource[B] to return from the flatMap method. This needs to be done by the caller, where the type of B is known, and where the desired implicit conversion functions are in scope. There, map() can be used with a f:A=>B, which is converted to f:A=>Resource[B].
sealed trait Resource[+T] { val value: T def close: Unit def use[X](f: T => X) = try {f(value)} finally {close} def map[B](f: T => Resource[B]): Resource[B] = use(f) def flatMap[B](f: T => B): Resource[B] = error("Compiler can't use the generic type 'B' to find an implicit function B => Resource[B]") def flatMapSpecific(f: T => java.io.InputStream): Resource[java.io.InputStream] = { import Resource._ val b = use(f) b } } import java.io.InputStream object Resource { def resource[T](t: T, c: => Unit) = new Resource[T] { val value = t def close = c } implicit def InputStreamResource(t: InputStream) = resource(t, t.close) } object Example { import Resource._ def main(args: Array[String]) { val in = new java.io.FileInputStream("/etc/passwd") println(in.map(copyStream).map(copyStream) use (_.read.toChar)) } def copyStream(i: InputStream): InputStream = { new ByteArrayInputStream(Array(i.read.toByte)) } }