Friday 30 July 2021

Scala Exception handling best practices

It's very good to be an optimistic person in real life however while programming you need to be a little pessimist to handle a situation where your code does not work as per expectation due to some problem which has nothing to do with your code or some time because of missing some corner cases while coding.  

So how do Exceptions helps us? By providing us following information in case of failure :
  • What went wrong? (exception.getMessage)
  • Where did it go wrong? (exception.getStackTrace)
  • Why it went wrong? (exception.getCause)
So in this post, I will try to cover two scenarios:
  1. How Scala signals something went wrong (Exceptions)
  2. Various ways of handling Exception cases
Before going into implementation details, I would like to throw light on some facts about Exceptions handling in Scala :
  • Scala does not have concept of Checked and Un-Checked Exceptions
  • Scala does not allow you to put throws clause in method signature as it does not differentiate between Checked and Un-Checked Exceptions.
  • You can achieve throws functionality using @throws annotation. eg. @throws(classOf[IOException]). However, this won't force Scala caller method to handle thrown Exception. This will only force Java caller method to handle that Exception.
  • Scala supports Java way of Exception handling ie. try-catch. However not recommended way in Scala.
  • Scala treats Exceptions as Case classes so you can apply Pattern matching on exceptions so you can handle all Exceptions in a single catch block.
  • Catching Throwable in Scala is deadlier than doing same in Java.
Scala supports following ways of handling Exceptions :
  1. try-catch
  2. Try
  3. Either[Left, Right]
  4. Scala Futures comes with support for exception handling
Let's look at these option in detail one by one :

1. try-catch

  • This is a non-functional way of handling exceptions. 
  • This is supported by Scala to provide backward compatibility with Java (This is my opinion :) )
  • Scala supports pattern matching on all Exception classes. So you can handle all exceptions in one catch block
  • Makes your code look ugly (Again my opinion)
  • Though Scala support try-catch block you should keep this option as last option to handle exceptions in Scala.

  def divide(number: Int, divider: Int): Int = number/divider

  def callDivide(): Option[Int] = {
    try {
      Option(divide(3, 0))
    } catch {
      case e: ArithmeticException => None
      case e: Exception => throw new Exception("Something went wrong.")
    }
  }

2. Try

  • Try provides you functional way to wrap your Exception prone code to handle Exceptions
  • It was originally written by Twitter developers and ported in Scala API's in Scala version 2.10 and also ported in version 2.9.3
  • You can imagine it as similar to Option or Either
  • This is monad which will result either in : 
  1. Success - In case of successful execution of code
  2. Failure - In case of error. Failure will wrap Throwable and return it to caller. 
  • One of the most important benefits of using Try is, it supports all higher-order functions (chaining operations) like map, flatmap, foreach etc. 

def divide(number: Int, divider: Int): Try[Int] = Try(number/divider)

def callDivide(): Int = {
    divide(4, 0) match {
      case Success(result) => result
      case Failure(e) => println("Exception occurred" + e.getMessage); 0
    }
  }

No comments:

Post a Comment