7

Idiomatic Error Handling in Scala

 3 years ago
source link: https://blog.knoldus.com/idiomatic-error-handling-in-scala/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Idiomatic Error Handling in Scala

Reading Time: 3 minutes

Error handling in Scala can just be written like Java. Put in a little bit of pattern matching magic and you are done. However, given a little use of Scala built-in terrific beauties it can be made much better. Let us have a look.

Let us look at a quick example

xxxxxxxxxx
def sayHello(any: Any) = {
    any match {
      case x: String => "Hello"
      case _ => throw new Exception("Huh!")
    }
  }                      //> sayHello: (any: Any)String
  def letMeSayHello = {
    sayHello(12)
  }                     //> letMeSayHello: => String
  letMeSayHello         //> java.lang.Exception: Huh!
                    //|     at SomeThing$$anonfun$main$1.sayHello$1(SomeThing.scala:11)

So we have a method called sayHello which misbehaves when you do not pass a string to it. If you call it without a string, it blows up and hence the letMeSayHello invocation blows up as well.

Ok, now traditionally we have been so used to try catch blocks that we put them around.

xxxxxxxxxx
def sayHello(any: Any) = {
    any match {
      case x: String => "Hello"
      case _ => throw new Exception("Huh!")
    }
  }                    //> sayHello: (any: Any)String
  def letMeSayHello = {
    try {
      sayHello(12)
    } catch {
      case e: Exception => "It's Ok if you dont want to say hello"
    }
 }                    //> letMeSayHello: => String
  letMeSayHello       //> res0: String = It's Ok if you dont want to say hello

Ok, so far so good. So we can really write Java in Scala 😉

Now let us see a better way (Idiomatic way!) of handling this

Scala comes with something called a Try. The Try type represents a computation that may either result in an exception, or return a successfully computed value. It’s similar to, but semantically different from the scala.util.Either type. Instances of Try[T], are either an instance of scala.util.Success[T] or scala.util.Failure[T].

Interesting, let us see how our code changes now

xxxxxxxxxx
import scala.util.Try
def sayHello(any: Any): Try[String] = {
    Try {
      any match {
        case x: String => "Hello"
        case _ => throw new Exception("Huh!")
      }
    }
  }                       //> sayHello: (any: Any)scala.util.Try[String]
  def letMeSayHello = {
    sayHello(12)
  }                       //> letMeSayHello: => scala.util.Try[String]
  letMeSayHello

So, we put a Try block around out sayHello method code. Now the method, letMeSayHello does not need to do explicit error handling. It gets back either Success(“Hello”) or Failure(java.lang.Exception: Huh!)

In the above scenario, it would get Failure(java.lang.Exception: Huh!) and you would be able to extract the value with

xxxxxxxxxx
 letMeSayHello.isSuccess   //> res0: Boolean = false

Now, there are various ways of dealing with this. You could pattern match on the boolean and take an action like

xxxxxxxxxx
 val result = letMeSayHello  //> result  : scala.util.Try[String] = Failure(java.lang.Exception: Huh!)
 if (result.isSuccess) result.get else "who cares"//> res0: String = who cares

OR you could simply do a getOrElse

xxxxxxxxxx
val result = letMeSayHello.getOrElse("who cares") //> result  : String = who cares
}

OR you could let the letMeSayHello method handle the success and failure

xxxxxxxxxx
 def letMeSayHello = {
    sayHello(12) match {
      case Success(result) => result
      case Failure(result) => "who cares"
    }
  }                       //> letMeSayHello: => String
  letMeSayHello           //> res0: String = who cares

OR you could get even fancier ! I like this one. The awesome recover mechanism

xxxxxxxxxx
def letMeSayHello = {
    sayHello(12) recover {
      case e: Exception => "who cares"
    }
  }                       //> letMeSayHello: => scala.util.Try[String]
  letMeSayHello.get       //> res0: String = who cares

The recover allows you to recover in case of failures with an alternate condition that you would want to execute which results in a success. Hence, in this case, we mentioned that either we would get a success by default or we would convert the error into a success scenario by writing a recover block so that we can confidently call letMeSayHello.get

You can find the gist here, on the Knoldus GitHub account. Have fun!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK