When you go a bit deeper into the meaning of monads and functional programming, you soon realize that functional programming in scala, essentially means that any List, Seq, Array, Map are actually all monads. Monads can be composed via binding (in scala this is done via map and flatMat), and can be chained via a series of flatMap to produce your final result.
But what about exceptions?
If you are not a theoretician, you will soon realize that exceptions do happen in scala, especially if you use vanilla programming via a mixture of Java and Scala libraries.
Scala comes to the rescue with an object which can transform any function which could potentially throw an exception into a monad. This is quite a big thing since by using this scala class you can render all you existing java and scala code completely side effect free. This means that you can encapsulate any code in a full functional programming paradigm.
The class is called Catch and it doesn't directly take an argument, but it defines a closure around your own code.
This class has two methods that are particularly useful for dealing with exceptions (and several more for catching multiple exceptions).
These are the opt and either methods:
def opt [U >: T] (body: ⇒ U) : Option[U]
def either[U >: T](body: ⇒ U): Either[Throwable, U]
The library provides a handful of method which can construct a Catch object for you.
Two of those are catching and allCatch
If you use the opt method it will return Some(result) if everything went okay, and None if the targeted exception was caught:
scala> import scala.util.control.Exception._
scala> catching(classOf[NumberFormatException]).opt( "apples".toInt )
res0: Option[Int] = None
scala> catching(classOf[NumberFormatException]).opt( "42".toInt )
res1: Option[Int] = Some(42)
You can then deal with this with map or filter or getOrElse or whatever else you use to deal with options.
The other useful method is either.
Either returns an instance of Left(exception) if an exception was thrown, and a Right(result) if it was not:
scala> catching(classOf[NumberFormatException]).either( "fish".toInt )
res2: Either[Throwable,Int] = Left(java.lang.NumberFormatException: For input string: "fish")
scala> catching(classOf[NumberFormatException]).either( "42".toInt )
res3: Either[Throwable,Int] = Right(42)
You can then use fold or map to an option or whatever else you like doing with either.
Note that you can define a single catcher and use it multiple times (so you don't need to create the catcher object every time you, for example, parse an integer):
scala> val catcher = catching(classOf[NumberFormatException])
catcher: util.control.Exception.Catch[Nothing] = Catch(java.lang.NumberFormatException)
res4: Option[Int] = Some(42)
res5: Option[Int] = None
If you want to catch everything, you can also use the handy allCatch method
scala> allCatch.opt( "42".toInt )
res2: Option[Int] = Some(42)
scala> allCatch.either( "apples".substring(8, 4) )
res11: Either[Throwable,java.lang.String] =
Left(java.lang.StringIndexOutOfBoundsException: String index out of range: -4)
scala> allCatch.either( "apples".toInt )
res5: Either[Throwable,Int] = Left(java.lang.NumberFormatException: For input string: "apples")