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?

3 Responses to “Clean-up resource with Scala”

  1. Quintesse Says:

    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 ;)

  2. Stephan Schmidt Says:

    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

  3. Jason Zaugg Says:

    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))
      }
    }
    

Leave a Reply