I need to initialise a set of vals, where the code to initialise them might throw an exception. I'd love to write:
try {
val x = ... generate x value ...
val y = ... generate y value ...
} catch { ... exception handling ... }
... use x and y ...
But this (obviously) doesn't work because x and y aren't in scope outside of the try.
It's easy to solve the problem by using mutable variables:
var x: Whatever = _
var y: Whatever = _
try {
x = ... generate x value ...
y = ... generate y value ...
} catch { ... exception handling ... }
... use x and y ...
But that's not exactly very nice.
It's also easy to solve the problem by duplicating the exception handling:
val x = try { ... generate x value ... } catch { ... exception handling ... }
val y = try { ... generate y value ... } catch { ... exception handling ... }
... use x and y ...
But that involves duplicating the exception handling.
There must be a "nice" way, but it's eluding me.
How about pattern matching?
val (x, y) = try generateX -> generateY catch { /*exception handling*/ }
or
val (x, y) = try (generateX, generateY) catch { /*exception handling*/ }
agilesteel's answer is fine if you just want to catch any exception that's thrown and do something procedural in the catch block. However you might want to deal with exceptions individually at a later time, in which case you might consider making your types an Option
or Either
.
A built-in way to do this is with a Catch
object. See the Exception docs.
How you use it will depend on what you want to happen when an exception occurs. For example
import util.control.Exception.allCatch
def handleInfinities(n: Int) = {
val x = allCatch.either { 100 / n } // Either[Throwable, Int]
val y = allCatch.either { 100 / (n - 1) }
Seq(x, y) map { case Left(_) => Int.MaxValue; case Right(z) => z }
}
Then handleInfinities(1)
gives
Seq[Int] = List(100, 2147483647)
Notice how the variable assignments and handling of exceptions are now completely separate.
A straightforward solution would be to define a wrapper function that uses by-name parameters.
def safely[T](f: => T): T = try { f } catch { /* exception handling */ }
// You'll have to play around with safely's type signature, depending on what
// you do in the exception handling
val x = safely { generateX }
val y = safely { generateY }
Or, if you're feeling really swanky, Scala 2.9 allows a partial function to be used as the exception handler.
val handler: PartialFunction[Throwable, Unit] = { /* exception handling */ }
val x = try { generateX } catch handler
val y = try { generateY } catch handler
On the whole, I would say that the first method is the most straightforward. But if you can write reusable bits of exception handlers that can be composed together using orElse
or andThen
, then the second method would be more appropriate.
import util.control.Exception._
def handler[A]: Catch[A] = handling(classOf[Exception]) by exceptionHandlingFunc
val x = handler[X] apply { generateX }
val y = handler[Y] apply { generateY }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With