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
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.
isEmpty
: This method returnstrue
if the object isNone
.nonEmpty
: This method returnstrue
if the object isSome
.isDefined
: This method returnstrue
if 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 analternate
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:
filter
: This method returnsOption
if theOptions
value isSome
and the function supplied returnstrue
.exists
: This method returnstrue
if theOption
is set and the supplied function returnstrue
.forall
: Behaves the same as theexists
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.