Something like that is a good idea, but I'd make it a method:
def cleanly[A,B](resource: => A)(cleanup: A => Unit)(code: A => B): Option[B] = {
try {
val r = resource
try { Some(code(r)) }
finally { cleanup(r) }
} catch {
case e: Exception => None
}
}
(note that we only catch once; if you really want a message printed in one case and not the other, then you do have to catch both like you did). (Also note that I only catch exceptions; catching Error
also is usually unwise, since it's almost impossible to recover from.) The method is used like so:
cleanly(new FileOutputStream(path))(_.close){ fos =>
Iterator.continually(in.read).takeWhile(_ != -1).foreach(fos.write)
}
Since it returns a value, you'll get a Some(())
if it succeeded here (which you can ignore).
Edit: to make it more general, I'd really have it return an Either
instead, so you get the exception. Like so:
def cleanly[A,B](resource: => A)(cleanup: A => Unit)(code: A => B): Either[Exception,B] = {
try {
val r = resource
try { Right(code(r)) } finally { cleanup(r) }
}
catch { case e: Exception => Left(e) }
}
Now if you get a Right
, all went okay. If you get a Left
, you can pick out your exception. If you don't care about the exception, you can use .right.toOption
to map it into an option, or just use .right.map
or whatever to operate on the correct result only if it is there (just like with Option
). (Pattern matching is a useful way to deal with Either
s.)