11

What is the analogous in manners of scala Try to:

timer.start()
try {
  doThis()
} finally {
  timer.cancel()
}
Jas
  • 14,493
  • 27
  • 97
  • 148
  • If you are using something implements the java.lang.AutoCloseable, you can use the code at the answer below to implement a standard "finally" pattern. You can also easily refactor the code in the answer below to retarget it to your specific resource API: http://stackoverflow.com/questions/4604237/how-to-write-to-a-file-in-scala/34277491#34277491 – chaotic3quilibrium Oct 01 '16 at 22:28

4 Answers4

13

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.

Régis Jean-Gilles
  • 32,541
  • 5
  • 83
  • 97
  • The second code block is not equivalent. `Try.map` only maps the `Success` case, so if an error occurs, `timer.cancel()` would never get called. – Michael Zajac Jun 03 '14 at 22:27
  • Now that's embarrasing, I don't know how I managed to be so stupid. Let's say it's because I'm sick today, and forget that. Thanks for spotting this. – Régis Jean-Gilles Jun 04 '14 at 06:28
6

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.

Norbert Radyk
  • 2,608
  • 20
  • 24
  • Generally you'll want to return the `Try` value – Régis Jean-Gilles Jun 03 '14 at 06:38
  • Agreed, in majority of cases you'd like to know when the exception is thrown and do something with this (therefore my last statement in the response) rather than just blindly ignore it (even if that's only acknowledging the exception being thrown). I've updated my answer to make it more precise. – Norbert Radyk Jun 03 '14 at 06:43
  • note that on fatal error (such as OOM) the "finally" code won't be executed, as fatal error are not being caught by `Try`. But such errors should make the whole app fail anyway. – Juh_ Nov 15 '19 at 08:51
1

How about

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

?

d-_-b
  • 4,142
  • 6
  • 28
  • 43
1

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.

DBear
  • 312
  • 2
  • 9