Exception Handling in Scala
By: Ashwini Anand
Tags: Scalatry, Exception Handling, Scala Programming, Scala, Exception

When I started working on scala I had a tough time understanding how exception handling works in scala. I had some background in java and javascript and I had exclusively worked with imperative codebases till that time.

When I look back I think my understanding about exception handling in scala came in stages: In the first stage I figured out what are the different ways of exception handling in scala. After this stage I knew what are different ways but I had a difficult time using those appropriately. During this time I started asking questions: Why do we need more than one way to handle exceptions? Where to use which one ? How to make the application behave properly in case of unexpected conditions and at the same time code should not become too messy for others to understand ? etc.

In this blog we will first briefly look into what are the different ways of exception handling in scala and when shall we use those. Next we will see some more points which are like set of best practices regarding Exception handling in scala.

There are predominantly 3 ways to handle exceptions in scala: Try, Option and Either.

Try :

From scaladoc The Try type represents a computation that may either result in an exception, or return a successfully computed value . A successful completion of Try[T] will result in Success[T] while failure results in Failure[T], where T is the result type. Failure wraps non-fatal exceptions. An example will look like as below:

val res = Try { /*some computation which can fail*/}

res match{

case Success(value) => SuccessResponse(value)

case Failure(exception) => {

Logger.error(" Your Failure Message",exception)

FailedResponse(exception.getMessage)

}

}

SuccessResponse and FailedResponse are case classes.

Use Try when you care about success or failure and also you care about matching the type of Throwable, getting error messages or state.

Option:

Option[T] is a container for Zero (None) or One element (Some[T])of type T.

One of the examples is the get method of Map which returns Option[T].

Use Option when you only care about success or failure but do not care about any error message or state.

Either:

Either[A,B] allows you to return one of two different types. Conventionally, Right is success and Left is failure.

Try can be thought of as Either with Left as Throwable and Right as result . Or Similarly Option can be thought of as Either where Left represents failure (by convention) and Right is akin to Some.

An example will look like as below:

trait GenericUserError

case class UserNotFound(id: Long) extends GenericUserError

case class UserAccountBlocked(id: Long) extends GenericUserError

case class User(id: Long, name: String)

def getUser: Either[GenericUserError, User] = ???

getUser match{

case Left(UserNotFound(id)) => println(s"user $id not found")

case Right(user) => println(s"username is ${user.name}")

}

I have found some people have issues with using Either in codebase and I think they are right because of the following reasons:

If we use Either for error handling, there is a certain assumption that Left is for Failure and Right is for Success cases. It’s easy to go wrong with this assumption in a large codebase with many people working. Either can do much more than simply error handling. In fact there is already an Either whose one of its two types is fixed for failure and another is for success. We should be using that for Exception handling. That is nothing but Try.

Either is not easily composable (no flatMap). In scala 2.12, there is a flatMap on Either which is right biased. Try and Option have been monadic in nature since their inception.

In case we want to return two results (success result and failed result) for a computation, Either can be used to simulate the same effect as try-catch block of java. But I think it’s better to subclass (using trait, case class, case object) our result types and make consumers handle all result types in such scenarios (rather than using Either).

After we have seen the different ways of error handling, below is a non exhaustive set of best practices for error handling in scala: