Start learning free

LESSON 105 OF 414 · ARRAYS I

Option in Scala: Some, None and Safe Map Lookups

Lesson Read the full explanation

What should a lookup return when the key isn't there? Many languages answer "null" and let you find out at runtime, the hard way. Scala answers with a real type: Option[A]. An Option is a box that is in exactly one of two states — Some(value) when there is a value, or None when there isn't. Because the box is part of the type, the compiler makes absence impossible to overlook: Map.get gives you an Option[String], not a String, so you cannot pretend the lookup always succeeds.

val capitals = Map("France" -> "Paris", "Japan" -> "Tokyo")

capitals.get("Japan")    // Some(Tokyo)  — an Option[String], not a String
capitals.get("Brazil")   // None         — no crash, just a value saying "nothing here"

Two everyday ways to open the box:

// 1. getOrElse — the value, or your fallback (safe)
capitals.getOrElse("Brazil", "unknown")   // "unknown"

// 2. pattern match — handle BOTH states explicitly (safe, and unwraps for you)
capitals.get("Japan") match {
    case Some(city) => println(s"got $city")   // city is the plain String inside
    case None => println("nothing there")
}

The trap:

Scala still lets you demand a value directly — capitals("Brazil") (the parenthesis lookup) skips the Option and just trusts the key exists. When it doesn't, the program dies:

java.util.NoSuchElementException: key not found: Brazil

Its twin is calling .get on the Option itself — capitals.get("Brazil").get compiles happily and crashes the same way:

java.util.NoSuchElementException: None.get

That is the exact crash Option exists to prevent. The rule experienced Scala developers follow: let lookups give you an Option, then leave the box through getOrElse or a match — never through a blind .get.


Assignment
Follow the Coding Tutorial and handle one present and one absent key, every safe way:

  • build val capitals = Map("France" -> "Paris", "Japan" -> "Tokyo"),
  • look up "Japan" (exists) and "Brazil" (doesn't) with .get, printing the raw Options,
  • print both again through getOrElse(key, "unknown") — plain values, no wrapper,
  • pattern match on each Option: case Some(city) for the hit, case None for the miss.

The exact expected output is:

found = Some(Tokyo)
missing = None
Japan or default = Tokyo
Brazil or default = unknown
Japan -> Tokyo
Brazil -> not found

Hint
Print with string interpolation: println(s"found = $found") shows the whole box (Some(Tokyo) or None), while case Some(city) => hands you the unwrapped city to print on its own.


STEP 1 OF 11
    Ctrl + Enter to check

    SCALA · ARRAYS I

    Option In Scala in Scala, explained

    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 type A;
    • 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 skips Option and throws java.util.NoSuchElementException: key not found: Brazil.
    • capitals.get("Brazil").get compiles fine but throws java.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

    1. Build the map. "France" -> "Paris" creates a pair, and Map(...) collects the pairs. It is stored in a val named capitals.
    2. Look up "Japan" with get and store it in found, whose type is Option[String].
    3. Print found with an s string. Interpolating an Option prints its wrapper form, so the line reads found = Some(Tokyo).
    4. Look up "Brazil" into missing and print it the same way. The line reads missing = None.
    5. Print both lookups through getOrElse with the fallback "unknown". Inside an interpolated string, a whole expression needs braces, ${...}, while a bare $ takes only a simple name.
    6. Match on found and then on missing, each time with a Some(city) case and a None case. For found the first case runs; for missing the 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 either Some(value) or None; the type itself says a value may be absent.
    • Map.get returns an Option; map(key) throws if the key is missing.
    • Prefer getOrElse for a fallback and match when each case needs its own code.
    • A blind .get on an Option brings the crash back when it is None.
    Keep going with ScalaAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free