analogous Try block to try/finally block in scala

Despite other answers saying otherwise, the correct answer is a Try cannot be completely analogous to a traditional try...finally. The reason for this is Try will ignore certain types of exceptions including InterruptedException, ControlThrowable, and others.

Consider if you use Try as recommended by other answers:

lock.lock()
val t = Try(doStuff()) // Doesn't absorb all types of exceptions!
lock.unlock() // THIS MIGHT BE SKIPPED!!!
t.get

If the thread is interrupted during doStuff(), then an InterruptedException will be thrown, and Try will rethrow it instead of creating a Failure! This will cause lock.unlock() to be skipped (and you'll probably deadlock). The same will happen if doStuff() invokes a lambda that invokes a non-local return (which throws a sub-type of ControlThrowable).

This is likely why the standard Scala library does not include a finally method in the Try class -- because it wouldn't work properly.

However, there is some good news. Scala 2.13 introduces Using which essentially performs a try-with-resources. We can trick this into doing a more general try...finally by using a dummy resource:

Using(lock.lock())(_ => doStuff())(_ => lock.unlock())

And if you don't have anything that is idiomatically performed before the try, then you can provide a Unit in the first parameter list:

Using(())(_ => doStuff())(_ => lock.unlock())

But in any case, Try is not going to do the job.


How about

Try {
  scala.util.control.Exception.ultimately {
    timer.cancel()
  } {
     doThis()
  }
}

?


Since Try won't throw an exception in your program flow I believe just write the following:

timer.start()
Try(doThis())
timer.cancel()

You can assign Try(doThis()) to a value and process it further if you wish to process the exception (rather than blindly ignoring it) or the call result.


Given that an exception inside a Try simply creates a Failure value (as opposed to transferring control to an outer catch block when using try), the code in your original finally block would just need to be executed after the Try. In other words, this will do:

timer.start()
val result = Try{
  doThis()
}
timer.cancel()
result

As far as I know there is no built-in shortcut that would allow to avoid capturing result just to return it as is.