Throwing exceptions in Scala, what is the “official rule”,

I am following the Scala course on Coursera. I started reading the Scala Odersky book.

What I often hear is that it is not recommended to throw exceptions into functional languages, because it interrupts the control flow, and we usually return Either with an error or success. It seems that Scala 2.10 will provide Try, which goes in that direction.

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

Finally, I am a little confused because I would like to follow the best practices, but they are not clear, and it seems that the language goes both ways ...

Can someone explain to me what should I use in this case?

+46
scala exception try-catch either
Oct. 14 '12 at 20:33
source share
2 answers

The main focus is the use of exceptions for something truly exceptional **. For a "normal" failure, it is much better to use Option or Either . If you interact with Java where exceptions are thrown when someone sneezes incorrectly, you can use Try to stay safe.

Take a few examples.

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

Now suppose you are writing a program in which the user must enter a file name. Now, if you are not going to immediately get a program when something goes wrong, 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 a string with numbers separated by spaces.

 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 } 

Thus, you have three ways (at least) to solve various types of failures: Option so that it works / does not run, in cases where the behavior is not expected, and not a shocking and alarming failure; Either , when everything may work or not (or, indeed, in any case, when you have two mutually exclusive parameters), and you want to keep some information about what went wrong; and Try , when you do not want the whole headache of exception handling to be done on your own, but you still need to interact with code that is exception-happy.

By the way, exceptions lead to good examples - so you will find them more often in a textbook or study material than elsewhere, I think: examples of textbooks are very often incomplete, which means that serious problems that can usually be prevented by careful the design must be flagged with an exception.

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

** Edit: Exceptions (no stack trace) are also used for control flow in Scala - they are actually a pretty efficient mechanism, and they include things like the library-set break and return expressions that return from your method, even if the control actually went into one or more closures. Basically, you don’t have to worry about this yourself, except that catching all Throwable not such a super idea, as you might be mistaken for one of these control flow exceptions.

+43
Oct. 14 '12 at 21:00
source share

So, this is one of those places where Scala specifically separates functional cleanliness from the ease of transition from / compatibility - with legacy languages ​​and environments, in particular with Java. Functional cleanliness is violated by exceptions because they violate referential integrity and make reasonable reasoning impossible. (Of course, non-ending recursions do the same, but several languages ​​are prepared to enforce constraints that will make it impossible). To maintain functional cleanliness, you use Option / Maybe / Lither / Try / Validation, all of which encode success or failure as a link-transparent type, and use the various higher-order functions that they provide, or the basic syntax of the syntax languages, to make things more clear. Or, in Scala, you can simply decide to distinguish functional purity, knowing that it can alleviate the situation in the short term, but more difficult in the long term. This is similar to using "null" in Scala, or mutable collections, or local "var" s. Mildly ashamed, and do not do this, but all in a pinch.

+10
Oct 14
source share



All Articles