The problem Option solves
Some operations can't always produce a value: looking up a key that isn't in a map, searching for an element that isn't there, parsing text that isn't a number. Many languages signal "nothing" with null or an exception, and the compiler can't remind you to handle it. Scala puts absence into the type. A value of type Option[A] is exactly one of two things:
Some(value), when there is a value of typeA;None, when there isn't.
A Map[String, String] has a method get that returns Option[String], so you can't use the result as a plain String without first deciding what to do when it is missing.
val capitals = Map("France" -> "Paris", "Japan" -> "Tokyo")
capitals.get("Japan") // Some(Tokyo)
capitals.get("Brazil") // None
A miss is an ordinary value. Nothing is thrown, and you can store it in a val, print it or pass it to another function.
Opening the box safely
getOrElse
getOrElse returns the wrapped value, or your fallback when there is none. The result is a plain String:
println(capitals.getOrElse("Japan", "unknown")) // Tokyo
println(capitals.getOrElse("Brazil", "unknown")) // unknown
On a map you can call getOrElse(key, default) directly, as above. The same method exists on an Option itself, with only the default as its argument: capitals.get("Brazil").getOrElse("unknown").
Pattern matching
A match handles both cases explicitly. In the Some(city) case, city is a new name bound to the value inside the box:
capitals.get("Japan") match {
case Some(city) => println(s"got $city")
case None => println("nothing there")
}
This prints got Tokyo. Because Option has exactly two shapes, the compiler can warn you if you forget one of the cases.
The unsafe ways
Scala still lets you demand a value directly, and both ways crash when it is missing:
capitals("Brazil")uses the bracket-style lookup, which skipsOptionand throwsjava.util.NoSuchElementException: key not found: Brazil.capitals.get("Brazil").getcompiles fine but throwsjava.util.NoSuchElementException: None.get.
The habit to build: let lookups give you an Option, then leave the box with getOrElse or a match, not with a blind .get. Use capitals("Japan") only when you know the key is there.
The lesson's exercise, step by step
- Build the map.
"France" -> "Paris"creates a pair, andMap(...)collects the pairs. It is stored in avalnamedcapitals. - Look up
"Japan"withgetand store it infound, whose type isOption[String]. - Print
foundwith ansstring. Interpolating anOptionprints its wrapper form, so the line readsfound = Some(Tokyo). - Look up
"Brazil"intomissingand print it the same way. The line readsmissing = None. - Print both lookups through
getOrElsewith the fallback"unknown". Inside an interpolated string, a whole expression needs braces,${...}, while a bare$takes only a simple name. - Match on
foundand then onmissing, each time with aSome(city)case and aNonecase. Forfoundthe first case runs; formissingthe second.
val capitals = Map("France" -> "Paris", "Japan" -> "Tokyo")
val found = capitals.get("Japan")
println(s"found = $found")
val missing = capitals.get("Brazil")
println(s"missing = $missing")
println(s"Japan or default = ${capitals.getOrElse("Japan", "unknown")}")
println(s"Brazil or default = ${capitals.getOrElse("Brazil", "unknown")}")
found match {
case Some(city) => println(s"Japan -> $city")
case None => println("Japan -> not found")
}
missing match {
case Some(city) => println(s"Brazil -> $city")
case None => println("Brazil -> not found")
}
The program prints:
found = Some(Tokyo)
missing = None
Japan or default = Tokyo
Brazil or default = unknown
Japan -> Tokyo
Brazil -> not found
More you can do with an Option
Besides getOrElse and match, an Option has methods you will meet often. map transforms the value inside and leaves None untouched, and isDefined and isEmpty tell you which shape you have:
println(found.map(_.length)) // Some(5)
println(missing.map(_.length)) // None
println(found.isDefined) // true
println(missing.isEmpty) // true
When you only need to know whether a key exists, capitals.contains("Japan") returns a plain Boolean.
Key takeaways
Option[A]is eitherSome(value)orNone; the type itself says a value may be absent.Map.getreturns anOption;map(key)throws if the key is missing.- Prefer
getOrElsefor a fallback andmatchwhen each case needs its own code. - A blind
.geton anOptionbrings the crash back when it isNone.
