Catching exceptions in Scala: Try type

It’s quite common to call functions that may throw exceptions. For instance, when a call is made to a web service, there could be a connection error which would throw an exception.

At first sight, we can think of using a try-catch block to handle the error in case the function explodes:

try {
  val userInfo = myWebServer.getUserInfo("Royston")
  userInfo.email
} catch {
  case e: ConnectionException => // do something
  case e: BadRequestException => // do something
  case _ => //do something
}

This is quite good because, if any exception is thrown, we will be able to catch and handle it. However, using the try block is not truly a functional approach. It leaves us with very few transformation options and nor are we allowed to decide at a later point in time on the action to perform in case an exception is raised.

Try type to the rescue

Try is a type that, given an action to perform, it can return either a Success with the result of that action or the exception thrown when trying to execute that action, but encapsulated in a Failure.

import scala.util.{ Try, Success, Failure }

Try(myWebServer.getUserInfo("Royston")) match {
  case Success(userInfo) => 
    userInfo.email
  case Failure(exception: ConnectionException) => 
    //do something
  case Failure(exception: BadRequestException) => 
    //do something
  case Failure(exception) => 
    //do something
}

As can be seen, by using pattern matching, we are able to define what to do depending on the result of executing the action. Now, as an example, we can use this type in the signature of a function:

def getUserInfo(username: String): Try[String] =
  Try(myWebServer.getUserInfo("Royston"))

This way we can leave the decision on what to do with the result of the function to a different part of the code.

Another advantage compared to using a try-catch block is that we can use the methods that the Try type API provides us with. These methods will allow us to evaluate or modify the value contained in a Try. For instance, we could use the map method:

def encrypt(value: String): String =
  value.map(_.toInt).mkString("-")

getUserInfo("Royston").map(_.email).map(_.encrypt)

In the previous piece of code, what we are doing is encrypting the email in case it is successfully obtained. In case an exception is thrown, no transformation will be carried out. The behaviour is identical to the one we already saw applied to Option type.

Besides, as it has a flatmap method, we can use it in for comprehension structures and combine several instances of the Try type.

def getEmail(username: String): Try[String] =
  getUserInfo(username).map(_.email)

def getAge(username: String): Try[Int] =
  getUserInfo(username).map(_.age)

for {
  email <- getEmail("Royston")
  age <- getAge("Royston")
  if age > 18
} yield s"User with $email is not under 18"

In this scenario, if any of the calls fails or if the user is underage, the execution flow will stop and no transformation will be performed. Otherwise, Try will be transformed.

Monads, monads everywhere

It would be quite wrong to conclude this post without our head being properly stimulated. We have seen that, by using the Try type, we can make our program more functional. As it is a monad complies some of the monad laws (what?), we can combine them in order to make our programs more expressive.

56248419

Well, there’s no need to panic. Further on, we’ll go deeper into monads and other functional abstractions and we’ll see they are not as complicated as they may seem.

Anuncios

One thought on “Catching exceptions in Scala: Try type

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s