Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initialising vals which might throw an exception

Tags:

scala

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.

like image 509
Paul Butcher Avatar asked Nov 11 '11 23:11

Paul Butcher


4 Answers

How about pattern matching?

val (x, y) = try generateX -> generateY catch { /*exception handling*/ }

or

val (x, y) = try (generateX, generateY) catch { /*exception handling*/ }
like image 113
agilesteel Avatar answered Sep 24 '22 18:09

agilesteel


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.

like image 21
Luigi Plinge Avatar answered Sep 22 '22 18:09

Luigi Plinge


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.

like image 40
leedm777 Avatar answered Sep 22 '22 18:09

leedm777


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 }
like image 23
missingfaktor Avatar answered Sep 24 '22 18:09

missingfaktor