This post reviews the different alternative mechanisms in Scala to handle errors. It also illustrates the applicability of the Option monad.

**Overview**

The Option monad is a great tool for handling error, in Scala: developers do not have worry about

*NullPointerException*or handling a typed exception as in Java and C++.

**Note**: For the sake of readability of the implementation of algorithms, all non-essential code such as error checking, comments, exception, validation of class and method arguments, scoping qualifiers or import is omitted
Let's consider the simple square root computation, which throws an exception if the input value is strictly negative (line 2). The most common "java-like" approach is to wrap the computation with a try - catch paradigm. In Scala catching exception can be implemented through the

*Try*monad (lines 7-9).1 2 3 4 5 6 7 8 9 10 | def sqrt(x: Double): Double = if(x < 0.0) throw MathException(s"sqrt: Incorrect argument $x") else Math.sqrt(x) Try ( sqrt(a)) match { case Success(x) => {} case Failure(e) => Console.println(e.toString) } |

This type of implementation put the burden on the client code to handle the exception. The Option monad provides developer an elegant to control the computation flow.

**Handling Option values**

The most common to handle a Scala option is to unwrap it. Let's consider the function

y = sin(sqrt(x))Clearly, there is no need to compute y if x is negative.

def sqrt(x: Double): Option[Double] = { if(x < 0.0) None else Math.sqrt(x) } def y(x: Double): Option[Double] = sqrt(x) match { case Some(y) => Math.sin(x) case None => None }

The computation of the square root is implemented by the method

This implementation is quite cumbersome because the client code has to process an extra Option. An alternative is to provide a default value (i.e 0.0) if the first computational step fails.

*sqrt*while the final computation of sin(sqrt(x)) is defined by the method*y*.This implementation is quite cumbersome because the client code has to process an extra Option. An alternative is to provide a default value (i.e 0.0) if the first computational step fails.

def y(x: Double): Double = Math.sin(sqrt(x)).getOrElse(0.0)

A more functional and elegant approach uses the

*map*higher order function to propagate the value of the*Option*.def y(x: Double): Double = sqrt(x).map(Math.sin(_)).getOrElse(0.0)

What about a sequence of nested options? Let's consider the function y = 1/sqrt(x). There are two types of errors:

*x < 0.0*for sqrt*x == 0.0*for 1/x

def y(xdef y(x: Double): Double = if(x < 1e-30) None else Some(1.0/(Math.sqrt(x)))

**for comprehension for options**

However anticipating the multiple complex conditions on the argument is not always possible. The for comprehensive for loop is an elegant approach to handle sequence of options.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | def inv(x: Double): Option[Double] = { if(Math.abs(x) < 1e-30) None else 1.0/x } def log(x: Double): Option[Double] = { if(x < 1e-30) None else Math.log(x) } def compose(x: Double): Double = (for { y <- sqrt(x) z <- inv(y) t <- log(z) } yield t).getOrElse(0.0) |

The objective is to compose the computation of a square root with the inverse function

For-comprehension is a monad that compose (cascading) multiple

*inv*(line 1) and natural logarithm*log*(line 6). The for comprehension construct (lines 11-15) propagates the result of each function to the next in the pipeline through the automatic conversion of option to its value. In case of error (None), the for method exists before completion.For-comprehension is a monad that compose (cascading) multiple

*flatMap*with a final*map*method.for { a <- f(x) // flatMap b <- g(a) // flatMap c <- h(b) // map } yield c

**References**

## No comments:

## Post a Comment