Scala: Examples of Option, Try, and Either

scala-logo-croped

Another quick Scala tip for you here. What is the difference between Option, Try, and Either? When should you use one versus the other?

 


 

Option
Because Scala will have None of your null values
Option is used for those times when you want to tell someone that a value is available (you have Some of it) or not (you have None of it). There are actually some videos here on the site (1, 2, 3, and 4) that get into the use of Option because in my experience it is one of the first things you should learn in Scala, so you can get away from returning null.

A quick example of option:

Say you want to greet someone and they may have logged in therefore caching their name. If they haven’t logged in, though, what should the cache return? The best thing for the cache to return is not a null value, but None. The cache has None of the user’s information. Thankfully when you create a new Option with the “Option([yourVariableHere])” syntax if you pass a null into Option, it automatically becomes None . Very elegant! Try the following in the REPL:

scala> :paste

case class MyCache(currentUsersName: String = null) {
  val name: Option[String] = Option(currentUsersName)
  def greetUser = println(s"Hello ${name.getOrElse("")}")
}

val currentCache = new MyCache

//press Ctrl-D once you've pasted the above in

// Exiting paste mode, now interpreting.

defined class MyCache
currentCache: MyCache = MyCache(null)

scala> currentCache.greet
Hello

scala> val currentCache = new MyCache("Scott")
currentCache: MyCache = MyCache(Scott)

scala> currentCache.greet
Hello Scott

coil_24484_sm

Try
Because Failure is not an Option
Try is similar to Option in that it also has only two possibilities. It can hold a Success or Failure. When it is a Success it holds a value of some expected type. When it is a Failure it holds a Throwable. You might wonder why you would want to wrap and return a Throwable instead of throwing it. There are a lot of scenarios where this makes sense. One is when you are calling a web service that returns a string and it times out. Maybe in that case you don’t want the program to exit the block on an exception but rather you want to return a string holding a simple error message instead. Try this in the REPL.

scala> val url = "http://api.hostip.info/get_json.php?ip=12.215.42.19"
url: String = http://api.hostip.info/get_json.php?ip=12.215.42.19

scala> val result = scala.io.Source.fromURL(url).mkString
result: String = {"country_name":"UNITED STATES","country_code":"US","city":"Aurora, TX","ip":"12.215.42.19"}

But let’s say we use a bad URL where we have omitted the “.php” part:

scala> val badUrl = "http://api.hostip.info/get_json?ip=12.215.42.19"
badUrl: String = http://api.hostip.info/get_json?ip=12.215.42.19

scala> val result = scala.io.Source.fromURL(badUrl).mkString
java.io.FileNotFoundException: http://api.hostip.info/get_json?ip=12.215.42.19
  at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1624)
  at java.net.URL.openStream(URL.java:1037)
  at scala.io.Source$.fromURL(Source.scala:141)
  at scala.io.Source$.fromURL(Source.scala:131)
  ... 33 elided

Now we can create a function using Try to make the exceptional case much nicer:

scala> import scala.util.Try
import scala.util.Try

scala> def callWebService(url: String): Try[String] = { Try(scala.io.Source.fromURL(url).mkString) }
callWebService: (url: String)scala.util.Try[String]

scala> callWebService(url)
res0: scala.util.Try[String] = Success({"country_name":"UNITED STATES","country_code":"US","city":"Aurora, TX","ip":"12.215.42.19"})

scala> callWebService(badUrl)
res1: scala.util.Try[String] = Failure(java.io.FileNotFoundException: http://api.hostip.info/get_json?ip=12.215.42.19)

Now you’ll notice that the response of callWebService is now a Success or a Failure. Let’s say we want to find the IP address using this approach:

scala>:paste
def retrieveIpAddress(url: String): String = {
  def translateResult(result: String) = {
    val resultSplit = result.replaceAll("[{|\"|}]","").split(",").flatMap(_.split(":"))
    resultSplit(resultSplit.length-1)
  }
  val serviceCall = callWebService(url)
  if(serviceCall.isSuccess)
    translateResult(serviceCall.get)
  else
    "Call failed with message: " + serviceCall.failed.get
}

//don't forget to hit Ctrl+D here

scala>retrieveIpAddress(url)
res74: String = 12.215.42.19

scala>retrieveIpAddress(badUrl)
res75: String = Call failed with message: java.io.FileNotFoundException: http://api.hostip.info/get_json?ip=12.215.42.19

What’s important here is that the final if statement above makes much more sense in context than the alternative of catching the exception, which would look something like this in Java. Notice that this is awkward because now we have a result variable that kind of hangs out uninitialized until the code figures out if something happened or not:

String result; //just hanging out uninitialized, yo!
try {
  result = callWebService(url)
} catch(Exception e) {
  result = "Call failed with message: " + e.toString
}

I much prefer the Scala way.

coil_24484_sm

Either
Either way is fine with Scala
As you might have guessed by now, either can also be only one of two things. In this case, since “Either” is the most abstract and generalized construct Scala has, it represents either Left or Right but what that means is generic, so you supply the types.

This might be a little abstract, but as you can see from the below example, it makes sense in practice. Here we have a guessing game. When you guess, you either can receive a Prize meaning you guessed the right value, or you’re going to get a GuessAgain that can tell you how close you are.

2 thoughts on “Scala: Examples of Option, Try, and Either

  1. Philip Schwarz (@philip_schwarz)

    Hello Scott,

    Nice post.

    FWIW, http://www.scala-lang.org/api/rc2/scala/Either.html says “Convention dictates that Left is used for failure and Right is used for success”. The way the author of FP in Scala puts it: “When we use it to indicate success or failure, by convention the Right constructor is reserved for the success case (a pun on “right,” meaning correct), and Left is used for failure.”

    Philip

Leave a Reply

Your email address will not be published. Required fields are marked *