Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Throwing exceptions in Scala, what is the "official rule"

I'm following the Scala course on Coursera. I've started to read the Scala book of Odersky as well.

What I often hear is that it's not a good idea to throw exceptions in functional languages, because it breaks the control flow and we usually return an Either with the Failure or Success. It seems also that Scala 2.10 will provide the Try which goes in that direction.

But in the book and the course, Martin Odersky doesn't seem to say (at least for now) that exceptions are bad, and he uses them a lot. I also noticed the methods assert / require...

Finally I'm a bit confused because I'd like to follow the best practices but they are not clear and the language seems to go in both directions...

Can someone explain me what i should use in which case?

like image 733
Sebastien Lorber Avatar asked Oct 14 '12 20:10

Sebastien Lorber


People also ask

How do you declare exceptions in Scala?

Exception handling is the mechanism to respond to and investigate the occurrence and cause of an exception. It is best practice in Scala to handle exceptions using a try{...} catch{...} block, similar to how it is used in Java, except that the catch block uses pattern matching to identify and handle exceptions.

How do you handle exceptions in Scala?

try/catch/finally A basic way we can handle exceptions in Scala is the try/catch/finally construct, really similar to the Java one. In the following example, to make testing easier, we'll return a different negative error code for each exception caught: def tryCatch(a: Int, b: Int): Int = { try { return Calculator.

How does throwing an exception work?

When a method declares that it throws an exception, it is not required to handle the exception. The caller of a method that throws exceptions is required to handle the exceptions (or throw them to its caller and so on) so that the flow of the program can be maintained.

How do you use throws in Scala?

Scala provides throws keyword to declare exception. You can declare exception with method definition. It provides information to the caller function that this method may throw this exception. It helps to caller function to handle and enclose that code in try-catch block to avoid abnormal termination of program.


2 Answers

The basic guideline is to use exceptions for something really exceptional**. For an "ordinary" failure, it's far better to use Option or Either. If you are interfacing with Java where exceptions are thrown when someone sneezes the wrong way, you can use Try to keep yourself safe.

Let's take some examples.

Suppose you have a method that fetches something from a map. What could go wrong? Well, something dramatic and dangerous like a segfault* stack overflow, or something expected like the element isn't found. You'd let the segfault stack overflow throw an exception, but if you merely don't find an element, why not return an Option[V] instead of the value or an exception (or null)?

Now suppose you're writing a program where the user is supposed to enter a filename. Now, if you're not just going to instantly bail on the program when something goes wrong, an Either is the way to go:

def main(args: Array[String]) {   val f = {     if (args.length < 1) Left("No filename given")     else {       val file = new File(args(0))       if (!file.exists) Left("File does not exist: "+args(0))       else Right(file)     }   }   // ... } 

Now suppose you want to parse an string with space-delimited numbers.

val numbers = "1 2 3 fish 5 6"      // Uh-oh // numbers.split(" ").map(_.toInt)  <- will throw exception! val tried = numbers.split(" ").map(s => Try(s.toInt))  // Caught it! val good = tried.collect{ case Success(n) => n } 

So you have three ways (at least) to deal with different types of failure: Option for it worked / didn't, in cases where not working is expected behavior, not a shocking and alarming failure; Either for when things can work or not (or, really, any case where you have two mutually exclusive options) and you want to save some information about what went wrong; and Try when you don't want the whole headache of exception handling yourself, but still need to interface with code that is exception-happy.

Incidentally, exceptions make for good examples--so you'll find them more often in a textbook or learning material than elsewhere, I think: textbook examples are very often incomplete, which means that serious problems that normally would be prevented by careful design ought instead be flagged by throwing an exception.

*Edit: Segfaults crash the JVM and should never happen regardless of the bytecode; even an exception won't help you then. I meant stack overflow.

**Edit: Exceptions (without a stack trace) are also used for control flow in Scala--they're actually quite an efficient mechanism, and they enable things like library-defined break statements and a return that returns from your method even though the control has actually passed into one or more closures. Mostly, you shouldn't worry about this yourself, except to realize that catching all Throwables is not such a super idea since you might catch one of these control flow exceptions by mistake.

like image 184
Rex Kerr Avatar answered Sep 21 '22 19:09

Rex Kerr


So this is one of those places where Scala specifically trades off functional purity for ease-of-transition-from/interoperability-with legacy languages and environments, specifically Java. Functional purity is broken by exceptions, as they break referential integrity and make it impossible to reason equationally. (Of course, non-terminating recursions do the same, but few languages are willing to enforce the restrictions that would make those impossible.) To keep functional purity, you use Option/Maybe/Either/Try/Validation, all of which encode success or failure as a referentially-transparent type, and use the various higher-order functions they provide or the underlying languages special monad syntax to make things clearer. Or, in Scala, you can simply decide to ditch functional purity, knowing that it might make things easier in the short term but more difficult in the long. This is similar to using "null" in Scala, or mutable collections, or local "var"s. Mildly shameful, and don't do to much of it, but everyone's under deadline.

like image 44
Dave Griffith Avatar answered Sep 18 '22 19:09

Dave Griffith