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/.
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> 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)
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.
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:
import scala.util.{Try => _, Success => Do, Failure => DoNot}
Solution
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> 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> 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> 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> 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> 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> 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> 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!
© Copyright 1994-2024 Michael Slinn. All rights reserved.
If you would like to request to use this copyright-protected work in any manner,
please send an email.
This website was made using Jekyll and Mike Slinn’s Jekyll Plugins.