The type scala.util.Try
is an operation that may result in either an exception or a valid output. Let’s explore a few of its usages, including single and chained calls to scala.util.Try.
The Try
operation will result in either a Success or Failure. To simplify, think about it an Either
for successes and failures instead of Left
and Right
, but proper for encapsulating exceptions instead of other arbitrary values.
How is it different than try/catch/finally?
The main difference between Try
and the try/catch/finally
is that the Try
operation allows you to pipeline (or pile-up, chain) operations that may result in errors, by using the map and flatMap operations and by applying pattern matching, whereas a try/catch/finally
focus on a specific error.
Single scala.util.Try operation
Let’s say we wouldn’t like to try parsing a String
into a java.time.Instant
, but we are not sure whether this will work. We can create a function that results in a Try[Instant]
:
def tryIt(value: String): Try[Instant] = Try(Instant.parse(value))
Now, we can invoke our function and use pattern matching to check whether it worked or not:
def simpleTry(): Unit = { tryIt("2020-11-17T00:00:00Z") match { case Success(instant) => println(s"Success at $instant!") case Failure(exception) => println(s"Not good: $exception") } }
The code above will print the successfully parsed instant to the console:
Success at 2020-11-17T00:00:00Z!
However, if we don’t provide a value that can be parsed:
def singleTryWithFailure(): Unit = { tryIt("not an instant") match { case Success(instant) => println(s"Success at $instant!") case Failure(exception) => println(s"Not good: $exception") } }
Then it will print the error:
Not good: java.time.format.DateTimeParseException: Text 'not an instant' could not be parsed at index 0
Chained scala.util.Try operations
Now things get a bit more interesting. Try
is excellent when chained, allowing to keep trying until you get the result, or finally hit the end of the chain and get the error.
Let’s look again at our function that may generate an error:
def tryIt(value: String): Try[Instant] = Try(Instant.parse(value))
And add yet another function, just to show that you can chain different functions, as long as they have the same return type:
def tryThat(): Try[Instant] = Try(Instant.now())
Now, let’s chain three calls, where the first two will fail but the last one will succeed. We can do this by using the orElse
function:
def chainedTry(): Unit = { tryIt("won't work") orElse tryIt("won't work either") orElse tryThat match { case Success(instant) => println(s"Success at $instant!") case Failure(exception) => println(s"Not good: $exception") } }
Which will print the current date and time:
Success at 2020-11-16T23:57:33.998Z!
But in case everything fails, you’ll get the latest exception. The following code:
def chainedTryWithFailure(): Unit = { tryIt("won't work") orElse tryIt("nothing will work") match { case Success(instant) => println(s"Success at $instant!") case Failure(exception) => println(s"Not good: $exception") } }
Produces this output;
Not good: java.time.format.DateTimeParseException: Text 'nothing will work' could not be parsed at index 0
Conclusion
There were just a few example of what Try
is capable of. Always check the documentation and explore the functions available in the type to find other ways of using it.
The source-code of this tutorial can be found on this GitHub repository.
I hope this helps. See ya!