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.
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:07Try
trait, which serves a similar purpose to Either, but also passes the exception information along in the case of a Failure.Try { "foo".toInt }
returnsFailure(java.lang.NumberFormatException: For input string: "foo")
. – KChaloux Jan 22 at 21:13