Mike Slinn

Try and try/catch/finally

— Draft —

Published 2014-12-27. Last modified 2016-01-23.
Time to read: 3 minutes.

Try is a utility class provided by the Scala runtime library. It either holds the value of a successful computation or it holds an Exception. The try/catch/finally language construct is also examined.

The sample code for this lecture can be found in courseNotes/src/main/scala/TryDemo.scala.

Try is a utility class provided by the Scala runtime library. It either holds the value of a successful computation or it holds a Throwable, which is most commonly an Exception or subclass thereof. There are two subclasses: Success and Failure. Here is an example:

Scala REPL
scala> import scala.util.{Try, Success, Failure}
import scala.util.{Try, Success, Failure}
scala>
def divide(dividend: Int, divisor: Int): Try[Int] = { | Try(dividend/divisor) match { | case Success(v) => | println(s"Result of $dividend/$divisor is: $v") | Success(v) | case Failure(e) => | println("You must have divided by zero or entered something that’s not an Int.") | println(e.getMessage) | Failure(e) | } | } divide: (dividend: Int, divisor: Int)scala.util.Try[Int]
scala>
divide(4, 2) Result of 4/2 is: 2 res0: scala.util.Try[Int] = Success(2)
scala>
divide(2, 0) You must have divided by zero or entered something that’s not an Int. / by zero res1: scala.util.Try[Int] = Failure(java.lang.ArithmeticException: / by zero)
Returning Try subtypes enables handling errors at a higher level of abstraction than the errors were encountered

It is often useful to initiate multiple tasks and then return a collection of results, some of which may have failures. To assist you to write this sort of code, you will need a way to manipulate the collections of successes and failures. The Trying Harder exercise in the Partial Functions lecture of the Intermediate Scala course shows you several methods of handling collections of Try.

Try does not provide the ability to chain clauses with an andThen combinator. That is unfortunate, because it would be nice to be able to write code like this.

Scala code
val x: Try[Int] = Try {
  2 / 0
} andThen {
  case Failure(ex) => println(s"Logging ${ex.getMessage}")
} andThen {
  case Success(value) => println(s"Success: got $value")
  case Failure(ex) => println(s"This just shows that any failure is provided to each chained andThen clause ${ex.getMessage}")
}

The Partial Functions lecture of the Intermediate Scala course shows how to enrich Try with an andThen combinator. The code is provided as part of the scalacourses-utils project on GitHub.

Exercise: Yoda He Is

“There is no try. There is only do, or do not.”

This exercise is a fun review of how import aliases work.

Rewrite the above Scala code using the following import:

Scala code
import scala.util.{Try => _, Success => Do, Failure => DoNot}

Solution

Scala code
object YodaHeis extends App {
  import scala.util.{Try => _, Success => Do, Failure => DoNot}
def divide(dividend: Int, divisor: Int): util.Try[Int] = { util.Try(dividend/divisor) match { case Do(v) => println(s"Result of $dividend/$divisor is: $v") Do(v) case DoNot(e) => println("You must have divided by zero or entered something that’s not an Int.") println(s"Error: ${e.getMessage}") DoNot(e) } }
println(s"divide(4, 2)=${divide(4, 2)}") println(s"divide(2, 0)=${divide(2, 0)}") }

NoStackTrace

It is somewhat expensive to fill in a new Exception’s stack trace. Scala offers a way to create a new Exception that does not contain a stack trace. An Exception without a stack trace should not be thrown because it might be difficult to determine the answers to questions like “who created this Throwable?” when debugging. However, this lighter-weight Exception can be useful to pass around, as we shall see in a moment.

Scala REPL
scala>  import util.control.NoStackTrace
import util.control.NoStackTrace
scala>
val e = new Exception("Help!") with NoStackTrace e: Exception with scala.util.control.NoStackTrace = $anon$1: Help!

The above creates an anonymous class, which means an extra class file is created every time the code is executed. These class files will pile up throughout the program’s life cycle in the JVM.

NoStackTrace Improved

We could improve the code by defining a class that mixes NoStackTrace into Exception. We discussed how mixing in traits could change the behavior of a Java or Scala class in the Scala Traits lecture.

Scala REPL
scala> class ExceptTrace(msg: String) extends Exception(msg) with NoStackTrace
defined class ExceptTrace
scala>
val e2 = new ExceptTrace("Boom!") e2: ExceptTrace = ExceptTrace: Boom!

The above is efficient. If your exceptions are invariant in all their properties each time, you could define a singleton object instead of a class instance for even greater efficiency.

Scala REPL
scala> object TheExceptTrace extends Exception("Boom!") with NoStackTrace
defined module TheExceptTrace 

try / catch / finally

try / catch / finally is a Scala language control structure. Let’s see it in action.

Scala REPL
scala> def divide2(dividend: Int, divisor: Int): Int = {
  try {
    dividend/divisor
  } catch {
    case e: ArithmeticException =>
      println(e.getMessage)
      0

    case e: Exception =>
      println(s"Did not see this one coming! $e")
      0
  }
}
def divide2(dividend: Int, divisor: Int): Int
scala>
divide2(4, 2) res23: Int = 2
scala>
divide2(2, 0) / by zero res24: Int = 0

As you can see, two problem cases are handled: ArithmeticException and all other Exceptions. The order that the exceptions are listed is significant. Each exception case can return a different value, and can contain nested try / catch clauses and optionally a finally clause. Unlike Java, the finally clause does not affect the returned value, and is only useful for side effects.

Scala REPL
scala> def divide3(dividend: Int, divisor: Int): Int = {
  try {
    dividend/divisor
  } catch {
    case e: ArithmeticException =>
      println(e.getMessage)
      0
case e: Exception => println(s"Did not see this one coming! $e") 0 } finally { println("The end") } }
def divide3(dividend: Int, divisor: Int): Int
scala>
divide3(4, 2) res23: Int = 2
scala>
divide3(2, 0) / by zero res24: Int = 0

The problem with the code above is that the return value does not indicate if a problem happened. Instead of returning a 0, it would be better to return the exception. Let’s create a better program by combining try / catch / finally with Try.

Scala REPL
scala> def divide4(dividend: Int, divisor: Int): Try[Int] = {
  try {
    Success(dividend/divisor)
  } catch {
    case e: Exception =>
      println(e.getMessage)
      Failure(e)
  }
}
def divide4(dividend: Int, divisor: Int): scala.util.Try[Int]
scala>
divide4(4, 2) res20: scala.util.Try[Int] = Success(2)
scala>
divide4(2, 0) / by zero res21: scala.util.Try[Int] = Failure(java.lang.ArithmeticException: / by zero)

We can express this more succinctly if we are willing to forego the printlns. Note the import statement.

Scala REPL
scala> import scala.util.{Try, Success, Failure}
scala> def divide5(dividend: Int, divisor: Int): Try[Int] = Try(dividend/divisor)
def divide5(dividend: Int, divisor: Int): scala.util.Try[Int]
scala>
divide5(4, 2) res20: scala.util.Try[Int] = Success(2)
scala>
divide5(2, 0) / by zero res21: scala.util.Try[Int] = Failure(java.lang.ArithmeticException: / by zero)

Simple, short, robust code!


* indicates a required field.

Please select the following to receive Mike Slinn’s newsletter:

You can unsubscribe at any time by clicking the link in the footer of emails.

Mike Slinn uses Mailchimp as his marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp’s privacy practices.