Skip to main content

Option

note

Also check out: Option

Change Value

map

Option.map is used to change value inside Option.

info

NOTE: Each map returns a new Option instance.

val maybeNumber1 = Option(1)
// maybeNumber1: Option[Int] = Some(value = 1)

maybeNumber1.map(n => n * 2)
// res1: Option[Int] = Some(value = 2)

maybeNumber1.map(n => n + 10)
// res2: Option[Int] = Some(value = 11)

maybeNumber1.map(n => n - 1)
// res3: Option[Int] = Some(value = 0)
val maybeNumber2: Option[Int] = Option.empty[Int]
// maybeNumber2: Option[Int] = None

maybeNumber2.map(n => n * 2)
// res5: Option[Int] = None

maybeNumber2.map(n => n + 10)
// res6: Option[Int] = None

maybeNumber2.map(n => n - 1)
// res7: Option[Int] = None
val maybeNumber1 = Option(1)
// maybeNumber1: Option[Int] = Some(value = 1)

maybeNumber1.map(n => s"Number is ${n.toString}")
// res9: Option[String] = Some(value = "Number is 1")
val maybeNumber2: Option[Int] = Option.empty[Int]
// maybeNumber2: Option[Int] = None

maybeNumber2.map(n => s"Number is ${n.toString}")
// res11: Option[String] = None

flatMap

If the return type of the function passed to Option.map is also Option, use Option.flatMap. Otherwise, you will end up having nested Options like Option[Option[A]]

  • Examples of nested Options
// for value.some to create Option[A] = Some(value) and none[A] to create Option[A] = None
import cats.syntax.all._
val maybeNumber = 1.some
// maybeNumber: Option[Int] = Some(value = 1)

maybeNumber.map(n => (n * 2).some)
// res13: Option[Option[Int]] = Some(value = Some(value = 2))

maybeNumber.map(n => (n + 10).some)
// res14: Option[Option[Int]] = Some(value = Some(value = 11))

maybeNumber.map(n => (n - 1).some)
// res15: Option[Option[Int]] = Some(value = Some(value = 0))
val maybeNumber2 = none[Int]
// maybeNumber2: Option[Int] = None

maybeNumber2.map(n => (n * 2).some)
// res16: Option[Option[Int]] = None

maybeNumber2.map(n => (n + 10).some)
// res17: Option[Option[Int]] = None

maybeNumber2.map(n => (n - 1).some)
// res18: Option[Option[Int]] = None
  • With flatMap
// for value.some to create Option[A] = Some(value) and none[A] to create Option[A] = None
import cats.syntax.all._
val maybeNumber = 1.some
// maybeNumber: Option[Int] = Some(value = 1)

maybeNumber.flatMap(n => (n * 2).some)
// res20: Option[Int] = Some(value = 2)

maybeNumber.flatMap(n => (n + 10).some)
// res21: Option[Int] = Some(value = 11)

maybeNumber.flatMap(n => (n - 1).some)
// res22: Option[Int] = Some(value = 0)
val maybeNumber2 = none[Int]
// maybeNumber2: Option[Int] = None

maybeNumber2.flatMap(n => (n * 2).some)
// res23: Option[Int] = None

maybeNumber2.flatMap(n => (n + 10).some)
// res24: Option[Int] = None

maybeNumber2.flatMap(n => (n - 1).some)
// res25: Option[Int] = None
  • More examples
// for value.some to create Option[A] = Some(value) and none[A] to create Option[A] = None
import cats.syntax.all._
val maybeNumber1 = 1.some
// maybeNumber1: Option[Int] = Some(value = 1)

maybeNumber1.flatMap { n =>
if (n < 0) none
else (n * 2).some
}
// res27: Option[Int] = Some(value = 2)
val maybeNumber2 = -1.some
// maybeNumber2: Option[Int] = Some(value = -1)
maybeNumber2.flatMap { n =>
if (n < 0) none
else (n * 2).some
}
// res28: Option[Int] = None
val maybeNumber3 = none[Int]
// maybeNumber3: Option[Int] = None
maybeNumber3.flatMap { n =>
if (n < 0) none
else (n * 2).some
}
// res29: Option[Int] = None

Get Value

You may think about using Option.get, but it's a bad practice which can cause a runtime exception. There are other safe ways to get the value out of Option.

Pattern Matching

Pattern matching is the most common and easy to understand way to get the value from Option.

import cats.syntax.all._
val maybeNumber1 = 999.some
// maybeNumber1: Option[Int] = Some(value = 999)

val number1 = maybeNumber1 match {
case Some(number) => number
case None => 0
}
// number1: Int = 999

println(s"number1 is ${number1.toString}.")
// number1 is 999.
val maybeNumber2 = none[Int]
// maybeNumber2: Option[Int] = None

val number2 = maybeNumber2 match {
case Some(number) => number
case None => 0
}
// number2: Int = 0

println(s"number2 is ${number2.toString}.")
// number2 is 0.

import cats.syntax.all._
val maybeNumber = 999.some
// maybeNumber: Option[Int] = Some(value = 999)

maybeNumber match {
case Some(number) => println(s"The number is ${number.toString}.")
case None => println("There is no number found.")
}
// The number is 999.
val maybeNumber2 = none[Int]
// maybeNumber2: Option[Int] = None

maybeNumber2 match {
case Some(number) => println(s"The number is ${number.toString}.")
case None => println("There is no number found.")
}
// There is no number found.

getOrElse

Alternatively, you can use Option.getOrElse.

import cats.syntax.all._
val maybeNumber1 = 999.some
// maybeNumber1: Option[Int] = Some(value = 999)

val number1 = maybeNumber1.getOrElse(0)
// number1: Int = 999

println(s"number1 is ${number1.toString}.")
// number1 is 999.
val maybeNumber2 = none[Int]
// maybeNumber2: Option[Int] = None

val number2 = maybeNumber2.getOrElse(0)
// number2: Int = 0

println(s"number2 is ${number2.toString}.")
// number2 is 0.

fold (Catamorphism)

If you want to change the value when it's Some and get the value, you can use fold. It's the same as map then getOrElse.

import cats.syntax.all._
val maybeNumber1 = 999.some
// maybeNumber1: Option[Int] = Some(value = 999)

val result1 = maybeNumber1.fold("No number2 is found.")(num => s"number1 is ${num.toString}.")
// result1: String = "number1 is 999."

println(s"Result1: $result1")
// Result1: number1 is 999.

which is equivalent to

import cats.syntax.all._
val maybeNumber1 = 999.some
// maybeNumber1: Option[Int] = Some(value = 999)

val result1 = maybeNumber1
.map(num => s"number1 is ${num.toString}.")
.getOrElse("No number2 is found.")
// result1: String = "number1 is 999."

println(s"Result1: $result1")
// Result1: number1 is 999.

import cats.syntax.all._
val maybeNumber2 = none[Int]
// maybeNumber2: Option[Int] = None

val result2 = maybeNumber2.fold("No number2 is found.")(num => s"number2 is ${num.toString}.")
// result2: String = "No number2 is found."

println(s"Result2: $result2")
// Result2: No number2 is found.

is equivalent to

import cats.syntax.all._
val maybeNumber2 = none[Int]
// maybeNumber2: Option[Int] = None

val result2 = maybeNumber2
.map(num => s"number2 is ${num.toString}.")
.getOrElse("No number2 is found.")
// result2: String = "No number2 is found."

println(s"Result2: $result2")
// Result2: No number2 is found.

Given Option[A], Option[A].filter takes a function A => Boolean. The function takes the value of type A in the Option[A].

  • When Option[A] is Some(a: A),
    • If the function returns true, filter returns the Some(a: A) again.
    • If the function returns false, filter returns None.
  • When Option[A] is None
    • It always returns None.

filter

import cats.syntax.all._
val maybeNumber1 = 1.some
// maybeNumber1: Option[Int] = Some(value = 1)

maybeNumber1.filter(n => n >= 0)
// res48: Option[Int] = Some(value = 1)

maybeNumber1.filter(n => n >= 1)
// res49: Option[Int] = Some(value = 1)

maybeNumber1.filter(n => n > 1)
// res50: Option[Int] = None
val maybeNumber2 = none[Int]
// maybeNumber2: Option[Int] = None

maybeNumber2.filter(n => n >= 0)
// res51: Option[Int] = None

maybeNumber2.filter(n => n >= 1)
// res52: Option[Int] = None

maybeNumber2.filter(n => n > 1)
// res53: Option[Int] = None

exists

import cats.syntax.all._
val maybeNumber1 = 1.some
// maybeNumber1: Option[Int] = Some(value = 1)

maybeNumber1.exists(n => n >= 0)
// res55: Boolean = true

maybeNumber1.exists(n => n >= 1)
// res56: Boolean = true

maybeNumber1.exists(n => n > 1)
// res57: Boolean = false
val maybeNumber2 = none[Int]
// maybeNumber2: Option[Int] = None

maybeNumber2.exists(n => n >= 0)
// res58: Boolean = false

maybeNumber2.exists(n => n >= 1)
// res59: Boolean = false

maybeNumber2.exists(n => n > 1)
// res60: Boolean = false

contains

import cats.syntax.all._
val maybeNumber1 = 1.some
// maybeNumber1: Option[Int] = Some(value = 1)

maybeNumber1.contains(0)
// res62: Boolean = false

maybeNumber1.contains(1)
// res63: Boolean = true

maybeNumber1.contains(2)
// res64: Boolean = false
val maybeNumber2 = none[Int]
// maybeNumber2: Option[Int] = None

maybeNumber2.contains(0)
// res65: Boolean = false

maybeNumber2.contains(1)
// res66: Boolean = false

maybeNumber2.contains(2)
// res67: Boolean = false