Understanding Options in Scala

  1. the Option Type in Scala
  2. Retrieve the Option’s Value in Scala
  3. the Option as a Container in Scala

In this article, we’ll learn how to work with optional data elements in Scala. Options in Scala help us to write robust code.

the Option Type in Scala

Option[T] in a Scala is a container storing zero or one value of a given type. Managing optional values using Scala’s Option, we can ensure two things:

  1. Type safety: We can parameterize the optional values.
  2. Functional awareness: The option type provides many functional capabilities that ensure fewer bugs are created in the program.

In Scala, an Option[T] is represented either by Some[T] or None[T] object. Here scala.Option is the base class which is abstract and extended, making the Option in Scala behave like a container.

Remember that the Option class and its subclasses require a concrete type. They can either be explicit or inferred like the following syntax.

Example:

val obj1: Option[Int] = None
val obj2 = Some(100)

Here obj1 and obj2 are objects of Option[Int]. One common question is how to check whether our Option is Some or None?

To solve this problem, we have the following functions.

  1. isEmpty: This method returns true if the object is None.
  2. nonEmpty: This method returns true if the object is Some.
  3. isDefined: This method returns true if the object is Some.

Retrieve the Option’s Value in Scala

We can use the get method to retrieve the Options value. If the get method is invoked on an object of None, then NoSuchElementException is thrown, also referred to as success bias.

Example Code:

val obj1: Option[Int]= ...

val v1 = if (obj1.isDefined)
{
  obj1.get
}
else
{
  0
}

The code above can also be written using pattern matching.

Example Code:

val obj1: Option[Int]= ...

val v1 = obj1 match {
  case Some(temp) =>
    temp
  case None =>
    0
}

We also have other methods like getOrElse and orElse to retrieve values.

  1. getOrElse: This method retrieves values if the object is Some; otherwise, it returns a default value.
  2. orElse: This method retrieves values if the object is Some; otherwise returns an alternate Option.

When the optional value is not set, the getOrElse method is very useful as we can return a default value.

Syntax:

val v1 = obj1.getOrElse(0)

the Option as a Container in Scala

As we have seen that Option is a container in Scala for another value, just as we traverse a List similarly, we can traverse an Option.

Let’s look at some methods for this container. Syntax of the Option.map method in Scala:

final def map[Y](f: (X) => Y): Option[Y]

Example Code:

val obj1: Option[Int] = Some(100)
assert(obj1.map(_.toString).contains("100"))
assert(obj1.map(_ * 2.0).contains(200))

val obj2: Option[Int] = None
assert(obj2.map(_.toString).isEmpty)

In the example above, we used the Option.map method to convert the contained values to another type. The Option.map method could also be used to affect the flow control of the program.

Example Code:

val obj1: Option[Int] = Some(10)
val obj2: Option[Int] = None

def mul(n: Int): Int = n * 2

assert(obj1.map(mul).contains(20))
assert(obj2.map(mul).isEmpty)

Here we created two objects of Option[Int], the first object has a value of 10, and the second one is empty. Then we defined a method mul that multiplies the input by 2.

Finally, we map obj1 and obj2 using the mul method. Mapping obj1 gives us Some(20), and mapping obj2 gives us None.

One thing to observe here is that in the second call, obj2.map(mul), the mul method is not called at all. We can flow control with filtering as well.

We have many methods to do so:

  1. filter: This method returns Option if the Options value is Some and the function supplied returns true.
  2. exists: This method returns true if the Option is set and the supplied function returns true.
  3. forall: Behaves the same as the exists method.

Using these functions, we can write the code very concisely.

Let’s have an example where we find the highest-scoring team.

Example Code:

 def highScoringTeam(playerA: Player, playerB: Player, tournament: Tournament): Option[(Player, Int)] =
 { getTopScore(playerA, tournament).foldRight(getTopScore(playerB, tournament)) {
      case (playerAInfo, playerBInfo) => playerBInfo.filter {
        case (_, scoreB) => scoreB > playerAInfo._2
      }.orElse(Some(playerAInfo))
    }
  }

In the above code, we have leveraged that Options can be used as collections. The only difference is that an Option can have at most one value.

Write for us
DelftStack articles are written by software geeks like you. If you also would like to contribute to DelftStack by writing paid articles, you can check the write for us page.