Scala Tutorials Part #16 - The Option type


The Option Type

Java programmers would be familiar with the NullPointerException which pops up when you access an object instance which has not yet been created yet.

This issue is not because of the Java library or the runtime, but it’s because of programmers writing crappy code. A language can offer some level of defense against human stupidity and that is what differentiates languages. Take a look at Null References : The Billion Dollar Mistake

Scala addresses this issue with the Option type. Let’s jump right in.

This is part 16 of the scala tutorial series. Check here for the full series.

Index

First line of defense in variables

Recall from the first chapter that variables can’t be just declared and left un-initialized.

But that does not prevent what java programmers have been doing all the time, i.e just leave them as null

  val x  = null

  println(x.toString)

This would result in the dreaded NullPointerException. We are still exposed to this issue which in medium/large code bases becomes very difficult to track . Assigning null to objects is often misused in imperative environments. The null in the scala language exists only for inter-operation with the java language environment. There is a better way to do this.

Introduction to the Option type

Option is not a type per se, it is syntactic sugar to a class that is underneath it.

In fact it is an abstract class which has two children Some and None. These three are not present in the scala type system hierarchy at all. Let’s take a look at how they work with a real world example.

Let’s try add data to an null ArrayList.

 ArrayList<Integer> data = null;

 data.add(1);

This would obviously lead to an exception.

Exception in thread "main" java.lang.NullPointerException
	at JavaRunner.main(JavaRunner.java:9)

If you are developing a customer facing application with some sort of a UI, throwing this error back to it would be absurd. What we would generally do is to catch this exception and then return some sort of a meaningful error message such as the “list not initialized” or something depending on the application.

try {

 ArrayList<Integer> data = null;
 data.get(1);

}

catch (NullPointerException e) {

 System.out.println("Array list not initialized");

}

This is handled in a completely different way in scala.

Option in scala

List is not exactly equal to a java ArrayList, but for demonstration purposes this should be fine. What we are trying to do is to find the first value that is greater than 100 and also the first value greater than 1000. Since we have a value that is greater than 100, the list returns the value by wrapping it into a Some type. In the case where there is no value greater than 1000 then return None.

Unlike Some which indicates the presence of a value of some type, None indicates non-existent values. None should not be confused with the Unit type which is used to represent the absence of a type itself.

This facility is of course provided by the List collection in scala, but we can build our own if we want to.

At this point, a typical java programmer would have a question in mind like “How do I declare a variable and not initialize it?” and the simple answer it to that is “You don’t”. You change your programming style not to include un-initialized variables in the code and not initialize them to null as well. We will be dealing with the latter part of it i.e null handling, the former part which is “Declaring a variable and initializing the variable along with the declaration” style as already seen in part 1 was a design choice taken by the scala language developers themselves.

Creating our own Option type

Let’s consider a case class User

case class User(id:Int,email:String)

Now there is a situation where we disallow email IDs belonging to gmail and yahoo domains.

def getFilteredEmailID(user:User) : Option[String] = {

    val disallowedDomains = "gmail.com,yahoo.com"

    if(!disallowedDomains.contains(user.email.split("@")(1))) Some(user.email) else None

}

The method splits the string using the @ character and gets the second index from the split array which gives the domain of the email address. This is now checked with the banned domains list and if there is a match then we return None else we return a Some. Below code example which consumes this method should make it clear.

val emailID = getFilteredEmailID(User(100,"[email protected]"))

  emailID match {
    case Some(x) => println(x)
    case None => println("Domain not allowed")
  }

We feed the User object to the getFilteredEmailID method which returns an Option[String]. It can be either Some or None depending on the computation. This is why Option is an abstract class since the result is either Some or None but never an Option itself at the top level. Some is a case class and None is a case object underneath.

What we are doing above is called pattern matching. We will see in detail about what pattern matching is in later tutorials. For now, it can be visualized as a simplified version of switch statements. The real advantage lies in everything being compile time and there is no possibility of a runtime NullPointerException.

A more simpler way to consume Option is to use the built in getOrElse method.

val emailID = getFilteredEmailID(User(100,"[email protected]"))

println(emailID.getOrElse("Banned domain"))

If None is returned then “Banned domain” is printed else the given email ID is printed. This can be used in simpler situations whereas pattern matching can be used in complex situations.

There are other ways in which an Option can be consumed, we will take a look at them at appropriate places in our journey through scala.

Conclusion

Scala option type

We saw the usage of the Option type in a very brief manner. There are several advantages of using the Option/Some/None pattern over java’s null. The main reason is you cannot say in a concrete manner when a NullPointerException will come since it is at runtime. Although you can carefully profile your code. In reality large code bases make this task tedious and not feasible. When using Scala’s Option, you can precisely reason that it can be either Some or None and we can deal with it rather than raising an exception and then catching it.

The Option type and its supporting classes Some and None can also be used to deal with situations other than null pointers. It is otherwise known as a monadic container which we will see more about in later tutorials.

For a spoiler on the upcoming tutorials, we will see how the unapply method works which is the opposite of apply method and then extractors finally leading to pattern matching.

Stay tuned


Tagged Under


Scala


Search this website...




Keeping up with blogs...

I blog occasionally and mostly write about Software engineering and the likes. If you are interested in keeping up with new blog posts, you should follow me on twitter where I usually tweet when I publish them. You can also use the RSS feed , or even subscribe via email below.

Feedio Subscribe


Share & Like

If you like this post, then you can either share/discuss/vote up on the below sites.



Thoughts ...

Please feel free to share your comments below for discussion

Blog comments powered by Disqus