Scala has a standard type named Option
for optional values. Such a value can be of two forms: Some(x)
, where
x
is the actual value, or the None
object which represents a missing value.
Optional values are produced by some standard operations on Scala’s collections. For instance, the get
method of
Scala’s Map
produces Some(value)
if a value corresponding to a given key has been found, or None
if the given
key is not defined in the Map
.
val capitals = Map("France" -> "Paris", "Japan" -> "Tokoyo")
val c1 = capitals get "France" // c1 will be Some("Paris")
val c2 = capitals get "USA" // c2 will be None
The most common way to take optional values apart is through a getOrElse
For instance:
def showCapital(c: Option[String]) = c.getOrElse("?")
val capitals = Map("France" -> "Paris", "Japan" -> "Tokoyo")
val c1 = showCapital(capitals get "Japan") // c1 will be "Paris"
val c2 = showCaptial(capitals get "USA") // c2 will be "?"
The Option
type is used frequently in Scala programs. Compare this to the dominant idiom in Java of
using null
to indicate no value. For example, the get
method of java.util.HashMapreturns
either a value
stored in the HashMap or null
if no value was found. This approach works for Java but is error prone
because it is difficult in practice to keep track of which variables in a program are allowed to be null
.
If a variable is allowed to be null
, then you must remember to check it for null
every time you use it.
When you forget to check, you open the possibility that a NullPointerException
may result at runtime.
Because such exceptions may not happen very often, it can be difficult to discover the bug during
testing.
For Scala, the approach would not work at all because it is possible to store value types in Hash
Maps, and null
is not a legal element for a value type. For instance, a HashMap[Int, Int]
cannot
return null
to signify “no element.”
By contrast, Scala encourages the use of Option
to indicate an optional value. This approach to optional
values has several advantages over Java’s. First, it is far more obvious to readers of code that a variable
whose type is Option[String]
is an optional String
than a variable of type String
, which may sometimes
be null. But most importantly, that programming error described earlier of using a variable that may
be null
without first checking it for null
becomes a type error in Scala. If a variable is of
type Option[String]
and you try to use it as a String
, your Scala program will not compile.
def makeInt(s: String): Option[Int] = {
try {
Some(s.trim.toInt)
} catch {
case e: Exception => None
}
}
val result = for {
x <- makeInt("1")
y <- makeInt("error")
z <- makeInt("3")
} yield x + y + z
result: Option[Int] = None
We see inside a for comprehension Options will short-circle at the first None. Recall this is because of the way for comprehension are semantic sugar for maps, and flatMaps.
val result = for {
x <- makeInt("1")
y <- makeInt("error")
z <- makeInt("3")
} yield x + y + z
// is equivalent to
val result = makeInt("1").flatMap{ x => makeInt("error").flatMap{ y => { makeInt("3").map {z => x + y + z }}}}
This terminates on the makeInt(“error”).flatMap => None