views:

574

answers:

3

i've defined 'using' keyword as following:

def using[A, B <: {def close(): Unit}] (closeable: B) (f: B => A): A =
  try { f(closeable) } finally { closeable.close() }

i can use it like that:

using(new PrintWriter("sample.txt")){ out =>
  out.println("hellow world!")
}

now i'm curious how to define 'using' keyword to take any number of parameters, and be able to access them separately:

using(new BufferedReader(new FileReader("in.txt")), new PrintWriter("out.txt")){ (in, out) =>
  out.println(in.readLIne)
}
+2  A: 

Unfortunately, there isn't support for arbitrary-length parameter lists with arbitrary types in standard Scala.

You might be able to do something like this with a couple of language changes (to allow variable parameter lists to be passed as HLists; see here for about 1/3 of what's needed).

Right now, the best thing to do is just do what Tuple and Function do: implement usingN for as many N as you need.

Two is easy enough, of course:

def using2[A, B <: {def close(): Unit}, C <: { def close(): Unit}](closeB: B, closeC: C)(f: (B,C) => A): A = {
  try { f(closeB,closeC) } finally { closeB.close(); closeC.close() }
}

If you need more, it's probably worth writing something that'll generate the source code.

Rex Kerr
+3  A: 

I've been thinking about this and I thought may be there was an other way to address this. Here is my take on supporting "any number" of parameters (limited by what tuples provide):

object UsingTest {

  type Closeable = {def close():Unit }

  final class CloseAfter[A<:Product](val x: A) {
    def closeAfter[B](block: A=>B): B = {
      try {
        block(x);
      } finally {
        for (i <- 0 until x.productArity) {
          x.productElement(i) match { 
            case c:Closeable => println("closing " + c); c.close()
            case _ => 
          }
        }
      }
    }
  }

  implicit def any2CloseAfter[A<:Product](x: A): CloseAfter[A] = 
    new CloseAfter(x)

  def main(args:Array[String]): Unit = {
    import java.io._

    (new BufferedReader(new FileReader("in.txt")), 
     new PrintWriter("out.txt"),
     new PrintWriter("sample.txt")) closeAfter {case (in, out, other) => 
      out.println(in.readLine) 
      other.println("hello world!")
    }
  }
}

I think I'm reusing the fact that 22 tuple/product classes have been written in the library... I don't think this syntax is clearer than using nested using (no pun intended), but it was an interesting puzzle.

edit: replaced the val assignment with case (in, out, other) as suggested by retronym.

huynhjl
Thats a nice trick, its a pity you sacrifice type safety in the contents of the tuple. The closure could be cleaner if you replace `x => val (in, out, other) = x` with `case (in, out, other) =>`
retronym
Thanks! I was wondering if there was something better than the val assignment. I'll update the answer.
huynhjl
+1  A: 

Here is an example that allows you to use the scala for comprehension as an automatic resource management block for any item that is a java.io.Closeable, but it could easily be expanded to work for any object with a close method.

This usage seems pretty close to the using statement and allows you to easily have as many resources defined in one block as you want.

object ResourceTest{
  import CloseableResource._
  import java.io._

  def test(){
    for( input <- new BufferedReader(new FileReader("/tmp/input.txt")); output <- new FileWriter("/tmp/output.txt") ){
      output.write(input.readLine)
    }
  }
}

class CloseableResource[T](resource: =>T,onClose: T=>Unit){
  def foreach(f: T=>Unit){
    val r = resource
    try{
      f(r)
    }
    finally{
      try{
        onClose(r)
      }
      catch{
        case e =>
          println("error closing resource")
          e.printStackTrace
      }
    }
  }
}

object CloseableResource{
  implicit def javaCloseableToCloseableResource[T <: java.io.Closeable](resource:T):CloseableResource[T] = new CloseableResource[T](resource,{_.close})
}
Dan Shryock