C# has using
with the IDisposable
interface. Java 7+ has identical functionality with try
and the AutoCloseable
interface
Here is my newer simple, understand at a glance, Scala ARM. This fully supports every use case I can think of including multiple resources and yield values. This uses a very simple for comprehension usage syntax:
class AutoCloseableWrapper[A <: AutoCloseable](protected val c: A) {
def map[B](f: (A) => B): B = {
try {
f(c)
} finally {
c.close()
}
}
def foreach(f: (A) => Unit): Unit = map(f)
// Not a proper flatMap.
def flatMap[B](f: (A) => B): B = map(f)
// Hack :)
def withFilter(f: (A) => Boolean) = this
}
object Arm {
def apply[A <: AutoCloseable](c: A) = new AutoCloseableWrapper(c)
}
Here's demo use:
class DemoCloseable(val s: String) extends AutoCloseable {
var closed = false
println(s"DemoCloseable create ${s}")
override def close(): Unit = {
println(s"DemoCloseable close ${s} previously closed=${closed}")
closed = true
}
}
object DemoCloseable {
def unapply(dc: DemoCloseable): Option[(String)] = Some(dc.s)
}
object Demo {
def main(args: Array[String]): Unit = {
for (v <- Arm(new DemoCloseable("abc"))) {
println(s"Using closeable ${v.s}")
}
for (a <- Arm(new DemoCloseable("a123"));
b <- Arm(new DemoCloseable("b123"));
c <- Arm(new DemoCloseable("c123"))) {
println(s"Using multiple resources for comprehension. a.s=${a.s}. b.s=${b.s}. c.s=${c.s}")
}
val yieldInt = for (v <- Arm(new DemoCloseable("abc"))) yield 123
println(s"yieldInt = $yieldInt")
val yieldString = for (DemoCloseable(s) <- Arm(new DemoCloseable("abc")); c <- s) yield c
println(s"yieldString = $yieldString")
println("done")
}
}
This is the code I use:
def use[A <: { def close(): Unit }, B](resource: A)(code: A ⇒ B): B =
try
code(resource)
finally
resource.close()
Unlike Java try-with-resources, the resource doesn't need to implement AutoCloseable. Only a close()
method is needed.
It only supports one resource.
Here is an example use with an InputStream
:
val path = Paths get "/etc/myfile"
use(Files.newInputStream(path)) { inputStream ⇒
val firstByte = inputStream.read()
....
}
Choppy's Lazy TryClose monad might be what you are looking for (disclosure: I'm the author). It is very similar to Scala's Try but automatically closes resources automatically.
val ds = new JdbcDataSource()
val output = for {
conn <- TryClose(ds.getConnection())
ps <- TryClose(conn.prepareStatement("select * from MyTable"))
rs <- TryClose.wrap(ps.executeQuery())
} yield wrap(extractResult(rs))
// Note that Nothing will actually be done until 'resolve' is called
output.resolve match {
case Success(result) => // Do something
case Failure(e) => // Handle Stuff
}
See here for more info: https://github.com/choppythelumberjack/tryclose
this one works for me really well:
implicit class ManagedCloseable[C <: AutoCloseable](resource: C) {
def apply[T](block: (C) => T): T = {
try {
block(resource)
} finally {
resource.close()
}
}
using it for example in this Apache Cassandra client code:
val metadata = Cluster.builder().addContactPoint("vader").withPort(1234).build() { cluster =>
cluster.getMetadata
}
or even shorter:
val metadata = Cluster.builder().addContactPoint("sedev01").withPort(9999).build()(_.getMetadata)
http://illegalexception.schlichtherle.de/2012/07/19/try-with-resources-for-scala/
Another implementation, probably more clean from "follow Java specifications" viewpoint, but also fails to support multiple resources
An improvement I can recommend to the approach you suggested, which is:
def autoClose[A <: AutoCloseable, B](resource: A)(code: A ⇒ B): B = {
try
code(resource)
finally
resource.close()
}
Is to use:
def autoClose[A <: AutoCloseable, B](resource: A)(code: A ⇒ B): Try[B] = {
val tryResult = Try {code(resource)}
resource.close()
tryResult
}
IMHO having the tryResult which is an Try[B]
, will allow you an easier control flow later.