Mike Slinn

Option, Some and None

— Draft —

Published 2014-01-11. Last modified 2019-04-22.
Time to read: 3 minutes.

Scala’s Option provides a way to eliminate null pointer exceptions.

The source code for this lecture is in courseNotes/src/main/scala/OptionDemo.scala.

Java methods often return null when there is no value to be returned. This is a leading source of runtime errors. Scala offers the Option container, which allows you to handle the two cases: Some value is returned, or the value is None.

The Option construct has been cleverly implemented, and it is actually a collection of zero or one items. That means you can iterate over its contents to work with any value that might be within. Because Option is a container, when you declare a variable to be of type Option you must also declare the type that may be contained by the Option.

For example, here is how we could declare a variable called maybeAnswer, which might contain an Int. The value stored in the variable is wrapped in a subclass of Option called Some, which contains the value we are interested in.

Scala REPL
scala> val maybeAnswer: Option[Int] = Some(42)
maybeAnswer: Option[Int] = Some(42) 

Scala 2.13 introduced the StringOps.toIntOption method, which attempts to parse an Int from a String. The toIntOption method either returns Some[Int] or None if the parse was unsuccessful.

Scala REPL
scala> val maybeAnswer: Option[Int] = "42".toIntOption
res0: Option[Int] = Some(42) 

We can obtain the value of an Option that has Some value with get, although that is not what you should usually do. More on that later.

Scala REPL
scala> maybeAnswer.get
res1: Int = 42 

We’ll look at the other StringOps.toXXXOption methods in a moment.

Similarly, we can declare a variable that might contain a String containing the name of our favorite type of chocolate, or None if we don’t like chocolate.

Scala REPL
scala> val maybeFavorite: Option[String] = None
maybeFavorite: Option[String] = None
scala>
maybeFavorite.getOrElse("Bleah!") res1: String = Bleah!

However, if we attempt to obtain the value of our favorite chocolate, we will get an error if we use get. Instead, we use getOrElse, which provides a default value if the Option contains None:

Scala REPL
scala> maybeFavorite.get
java.util.NoSuchElementException: None.get
scala>
maybeFavorite.getOrElse("Nope") res6: String = Nope

We can also perform an action if an Option contains Some value:

Scala REPL
scala> maybeAnswer.foreach { x => println(3 * x) }  // prints the value of 42 times 3
126
scala>
maybeFavorite.foreach { println } // does not print anything

If an Option has Some value, its isDefined method will return true; otherwise, if the Option has None value isEmpty will return true.

Scala REPL
scala> val object1 = Some("Hi there")
object1: Some[java.lang.String] = Some(Hi there)
scala>
object1.isDefined res11: Boolean = true
scala>
object1.isEmpty res12: Boolean = false
scala>
val object2 = None object2: None.type = None
scala>
object2.isDefined res13: Boolean = false
scala>
object2.isEmpty res14: Boolean = true

Scala 2.13’s Other StringOps.toXXXOption Methods

Scala 2.13 introduced better StringOps parsing methods in addition to toIntOption, which we have already seen.

Scala REPL
scala> "true".toBooleanOption
res3: Option[Boolean] = Some(true)
scala>
"false".toBooleanOption res4: Option[Boolean] = Some(false)
scala>
"true".toBooleanOption.foreach( if (_) println("Yes"); else println("No") ) Yes
scala>
"asdf".toBooleanOption.foreach( if (_) println("Yes"); else println("No") ) No output because foreach does not iterate over None
scala>
"asdf".toBooleanOption.map( if (_) "Yes"; else "No" ) res5: Option[String] = None
scala>
"123".toBooleanOption res6: Option[Boolean] = None
scala>
"1".toDoubleOption res7: Option[Double] = Some(1.0)
scala>
"asdf".toDoubleOption res8: Option[Double] = None

Wrapping Nulls with Option

Here is where Option gets interesting. First imagine that you want to retrieve the value of an environment variable.

Scala REPL
scala> Option(System.getProperty("os.name"))
res15: Option[java.lang.String] = Some(Windows 7) 

Because we are sure that the result is wrapped in a Some instance, we call get to retrieve it (we will discover soon that it usually is better to use map, foreach or other iterators instead of calling get).

Scala REPL
scala> Option(System.getProperty("os.name")).get
res16: java.lang.String = Windows 7 

If the environment variable is not set, Option will return None instead of null.

Scala REPL
scala> Option(System.getProperty("not.present"))
res17: Option[java.lang.String] = None

orElse

We can use orElse() to provide a default value if the Option’s value was None.

Scala REPL
scala> Option(System.getProperty("not.present")).orElse(Some("default"))
res3: Option[String] = Some(default)
scala>
Option(System.getProperty("os.name")).orElse(Some("default")) res4: Option[String] = Some(Linux)

The above returns an Option if the system property has a value or not. Now we can use get to retrieve whichever value comes back.

Scala REPL
scala> Option(System.getProperty("not.present")).orElse(Some("default")).get
res19: Option[java.lang.String] = default 

getOrElse

We can use getOrElse to retrieve the value of the Option, or the value of the getOrElse arguments if the Option’s value was None.

Scala REPL
scala> Option(System.getProperty("not.present")).getOrElse("default")
res18: String = default
scala> Option(System.getProperty("os.name")).getOrElse("default") res6: String = Linux

Option is actually a collection of zero or one items. This means that collection operations work on Option instances. This leads to useful Scala idioms, such as provided by Option.fold. We will discuss that in the next course. It is also helpful to think of Option as a container.

Exercise

Write a Scala console app that checks the value of an environment variable. If it is defined, print out its value, otherwise print out "undefined".

Solution

This solution can be found in courseNotes/src/main/scala/solutions/TupleAnswer.scala".

Scala code
object TupleAnswer extends App {
  def osProp(name: String): String = Option(System.getProperty(name)).getOrElse("undefined")
   if (args.length>=1)
    println(s"""Value of ’${args(0)}’ system property is ’${osProp(args(0))}’""")
}

To run it, type:

Shell
$ sbt "runMain solutions.TupleAnswer os.name"
Shell
$ sbt "runMain solutions.TupleAnswer user.home"

* 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.