3

I am curious why exception handling is a topic often ignored in Scala. Is it the goal of the language (or the style) to not rely on exception handling except for external input/code?

I was hoping the style guide had some discussion on exception checking/handling. Is there a good reference that I could consult to understand if there are any differences in handling exceptions in Scala vs. languages I'm more used to (C++/C#/Java/Ruby/etc)?

Would someone with some Scala years under their belt be willing to write down guidelines for exception handling that are Scala specific? or is the answer simply "do as you would in Java"? What is "the functional way"?

closed as too broad by gnat, user40980, GlenH7, Kilian Foth, Robert Harvey Jan 22 '15 at 23:13

Please edit the question to limit it to a specific problem with enough detail to identify an adequate answer. Avoid asking multiple distinct questions at once. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • 3
    This article provides a pretty good primer. Can you read it, and then edit your question to identify what you don't specifically understand? – Robert Harvey Dec 25 '14 at 19:58
  • 1
    The Functional way is to use Either. It returns either an instance of Left[L] or an instance of Right[R]. By convention, Left is used to represent failure and Right is used to represent success. – Robert Harvey Dec 25 '14 at 20:07
  • 1
    @RobertHarvey It's probably worth noting that Scala now provides a Try trait, which serves a similar purpose to Either, but also passes the exception information along in the case of a Failure. Try { "foo".toInt } returns Failure(java.lang.NumberFormatException: For input string: "foo"). – KChaloux Jan 22 '15 at 21:13
8

Good functional style does not need defensive coding nearly as much as the imperative style. try and catch is not the only control structure which is neglected in Scala (and other functional languages); for and while loops are endangered species and even if...else is used more selectively (although much more effectively, since it returns a value).

In the functional world, you match your code to the shape of the data. Or you choose a shape which gives your code the structure you want. This is true not only with Option and Either but List and Set and Map and more. All of them are paired with powerful Higher Order Functions - map, fold, filter etc.

To take a trivial example, if a Java or C++ coder wants to fetch the first (or all) of the items in a list and manipulate it, they either have to first check that the list is not empty or wrap it in try/catch to handle the error. If they have to deal with a collection of lists, the latter option is more likely to be chosen (and even less likely to match the context which might generate an error).

In Scala you can simply do this to the list of Ints called xs:

  xs.headOption map (_ * 2)
  // Returns twice the first item - if there is one - as an Option

or

  xs map (_ * 2)
  // Doubles everything in the list.  Returns a list.

If there is nothing in the list, nothing will happen. If you want something to happen, you can detect the failure and react.

  xs.headOption map (_ * 2) orElse Some(0)
  // Returns Some(twice the head item) if there is one or Some(0)
  xs.headOption map (_ * 2) getOrElse 0
  // Returns twice the head item or 0 if there is none

Even better, if you are dealing with a lists of lists, any one of which might be empty, mapping over each of them will produced results for every populated list and no problem at all with any empty ones. That is very powerful when dealing with large collections of unpredictable data.

What these functional types (Option and Either and List and Map and all the other - sorry to use the ugly word - monads) also offer, very importantly, is separation of concerns. Notice how I used map in all the above examples. In two examples it is Option.map, while in the other it is List.map. In all cases, map is doing the same thing: ¨Apply my function to the data inside that container, preserving the context¨. If it is a list context, you get a list of transformed data back. If it is a ¨may or may not exist¨ context, you may get your transformed object. If it is a ¨this could really go badly wrong¨ context, you either get your object or a chance to complain. And so on.

So you get a separation of concerns between the action you want to perform and the context in which it is applied. The big advantage is that if you decide to change the context (e.g. Set of unique objects rather than arbitrary List of objects), the rest of the code doesn´t need to change at all. map will still do the right thing (as will filter, fold and the rest).

Built-in imperative keywords like try..catch or if...else...if...else do not have that power. For a start, they have no real meaning of their own and have to be bodged together (distorting the code tangled within them) and they offer no guarantee. Imagine having used those to manage an arbitrary list of objects and then deciding you want to guarantee uniqueness. How much of that imperative code will you have to change? How can you be confident it will work?

Leave exceptions to deal with the truly unexpected and unsolveable. Functional types can be confident about their guarantees, to help you avoid the common and predictable errors.

  • 1
    I would add that exceptions should be used, as the word says, to handle exceptional situations, i.e. errors, not to build the normal flow of control. That's why a function that might not return any result should have a result type Option instead of modeling the no-result-found situation by throwing an exception. – Giorgio Jan 21 '15 at 19:12
  • Nice answer (+1), especially for explaining that monadic types like Option are a good substitute for options. I often see exception as a jump into the void, in the hope you land somewhere. Options are rather taking an escape door into a tunnel that guides you out to a safe place. You might want to mention continuations (also supported by Scala) as yet another way of dealing with failure, see e.g. Chapter 29 of cs.cmu.edu/~rwh/smlbook/book.pdf. – Giorgio Jan 21 '15 at 19:49
  • I do like Bob Harper, but it is amusing to see him quoted here, given that he thinks Monads are overrated, hates type classes and thinks very little of Scala - not least because it has both of those and not the best implementation of either ;) – itsbruce Jan 21 '15 at 20:56
  • You probably know Robert Harper better than I do. I only read the book I cited and I find it a very good reading. Of course he has his own views, which I cannot judge, since I am not an expert in FP, even though I like the subject a lot. – Giorgio Jan 21 '15 at 23:06
  • Although I like the Option/Either/etc, part of my has shivers when it comes to thinking about boxing/unboxing. I guess I will need to get over that. Is the use of twitter Try (scala.util.Try) also proper FP? – Will I Am Jan 22 '15 at 3:47
5

Scala encourages the use of constructs like options and futures for error handling. First of all, lazy evaluation makes exceptions problematic, because there is a different stack when a function is executed than when it's queued to execute. Scala isn't lazy by default, but it can be lazy when you choose.

Also, options and futures are much more powerful than exceptions. You can chain operations on them, and they aren't limited to propagating straight up the stack like exceptions are. You can store them in collections and reduce duplication in handler code.

Pretty much the only time you use exceptions in Scala is for errors you can't handle that will kill your program anyway.

  • I'm not sure I'd call Option more powerful than exceptions. A chain of operations that may each fail with Option needs to test every result to see if they succeeded. An exception only incurs a cost if it's thrown. Options also change the types of every intermediate expression even for the cases where it can be proven that the input will never cause a failure. In my opinion they have different uses rather than one being more powerful. – Doval Dec 25 '14 at 20:53
  • 3
    If the chain is built using map, flatmap or one of the other HoFs, testing every step is not necessary, @Doval. But you know that so I suppose you mean the testing inside map and flatmap. – itsbruce Jan 21 '15 at 17:51
  • 3
    I disagree that options are used for error handling: When I look for a number with a certain property and I find no number, it does not mean I have an error. My computation simply terminates and gives a correct answer: None. On the contrary, I would say that languages that do not have an option type encourage the use of exception to model an optional result, which is conceptually wrong. Exceptions are for errors, not for expected results. – Giorgio Jan 21 '15 at 19:16
  • 1
    @Doval: Sure, what you consider an error and what you consider a normal negative result depends on the context, but this does not mean you shouldn't make this distinction and use different constructs. BTW, IMO a very interesting reading can be found in Chapter 29 of cs.cmu.edu/~rwh/smlbook/book.pdf – Giorgio Jan 21 '15 at 19:37
  • 1
    @Giorgio That chapter is actually what I was thinking of when I mentioned the Eight queens puzzle. On that note, static safety isn't an issue in Java since it has checked exceptions as well as the usual unchecked exceptions (although it's still an issue in most other languages.) – Doval Jan 21 '15 at 19:51

Not the answer you're looking for? Browse other questions tagged or ask your own question.