Understanding Options 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:
- Type safety: We can parameterize the optional values.
- Functional awareness: The
optiontype 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.
isEmpty: This method returnstrueif the object isNone.nonEmpty: This method returnstrueif the object isSome.isDefined: This method returnstrueif the object isSome.
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.
getOrElse: This method retrieves values if the object isSome; otherwise, it returns a default value.orElse: This method retrieves values if the object isSome; otherwise returns analternateOption.
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:
filter: This method returnsOptionif theOptionsvalue isSomeand the function supplied returnstrue.exists: This method returnstrueif theOptionis set and the supplied function returnstrue.forall: Behaves the same as theexistsmethod.
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.
