Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the proper way to return from an exception in Scala?

Tags:

scala

In a non-functional language, I might do something like:

try {
  // some stuff
} catch Exception ex {
  return false;
}

// Do more stuff

return true;

In Scala, however, this pattern is clearly not correct. If my scala code looks like this:

try {
  // do some stuff
}
catch {
  case e: Exception => // I want to get out of here and return false
  )
}

// do more stuff

true

How do I properly do that? I don't want to use the "return" statement, of course, but I also don't want to drop through and "do more stuff" and eventually return true.

like image 578
Christopher Ambler Avatar asked Aug 19 '14 23:08

Christopher Ambler


1 Answers

You want to represent a computation that can either succeed or signal that an error has occurred. That's the perfect use case for the Try monad.

import scala.util.{ Try, Success, Failure }

def myMethod: Try[Something] = Try {
  // do stuff
  // do more stuff
  // if any exception occurs here, it gets wrapped into a Failure(e)
}

So you're returning a Try instead of a Bool, which is infinitely more clear and idiomatic.

Usage example:

myMethod match {
  case Success(x) => println(s"computation succeded with result $x")
  case Failure(e) => println(s"computation failed with exception $e.getMessage")  
}

If you don't even care about the exception, but you just want to return a value in case of success, you can even convert the Try to an Option.

def myMethod: Option[Something] = Try {
  // do stuff
  // do more stuff
  // return something
  // if any exception occurs here, it gets wrapped into a Failure(e)
}.toOption

myMethod match {
  case Some(x) => println(s"computation succeded with result $x")
  case None => println("computation failed")  
}

To respond to the question in the comments, you can do

Try {
  // do stuff
} match {
   case Failure(_) => false
   case Success(_) =>
     // do more stuff
     // true
}

although I would suggest to return something more meaningful than a Boolean, whenever it makes sense.

Of course this can be nested

Try {
  // do stuff
} match {
   case Failure(_) => false
   case Success(_) =>
     // do more stuff
     Try {
       // something that can throw
     } match {
       case Failure(_) => false
       case Success(_) =>
         // do more stuff
         true
     }
}

but you should consider putting the Try chunks into separate functions (returning a Try).

Ultimately, we can take advantage of the fact that Try is a monad, and do something like this

Try { /* java code */ }.flatMap { _ =>
  // do more stuff
  Try { /* java code */ }.flatMap { _ =>
    // do more stuff
    Try { /* java code */ }
  }
} match {
  case Failure(_) => false // in case any of the Try blocks has thrown an Exception 
  case Success(_) => true // everything went smooth
}
like image 117
Gabriele Petronella Avatar answered Dec 16 '22 06:12

Gabriele Petronella