Skip to main content

Option

note

Also check out: Option

Construct Option

.some for Some / none for None

Use .some and none[A] from cats instead of Some and None.

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

vs

Some(1)
// res2: Some[Int] = Some(value = 1)

none[Int]
// res3: Option[Int] = None

vs

None
// res4: None.type = None

Conditional Construction

Conditionally construct Option.

val num1 = 1
// num1: Int = 1
Option.when(num1 > 0)(num1)
// res6: Option[Int] = Some(value = 1)

val num2 = 0
// num2: Int = 0
Option.when(num2 > 0)(num2)
// res7: Option[Int] = None

val num3 = -1
// num3: Int = -1
Option.when(num3 > 0)(num3)
// res8: Option[Int] = None

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)
// res10: Option[Int] = Some(value = 2)

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

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

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

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

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

maybeNumber1.map(n => s"Number is ${n.toString}")
// res18: 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}")
// res20: 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)
// res22: Option[Option[Int]] = Some(value = Some(value = 2))

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

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

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

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

maybeNumber2.map(n => (n - 1).some)
// res27: 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)
// res29: Option[Int] = Some(value = 2)

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

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

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

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

maybeNumber2.flatMap(n => (n - 1).some)
// res34: 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
}
// res36: 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
}
// res37: Option[Int] = None
val maybeNumber3 = none[Int]
// maybeNumber3: Option[Int] = None
maybeNumber3.flatMap { n =>
if (n < 0) none
else (n * 2).some
}
// res38: 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)
// res57: Option[Int] = Some(value = 1)

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

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

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

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

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

exists

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

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

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

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

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

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

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

contains

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

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

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

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

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

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

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

collect

If you want to retain the value only if it's what you're looking for, or optionally transform the found value as if using map, you can use collect.

collect takes a PartialFunction and any non-matched value will be ignored and Option becomes None.

val maybeNum1 = Option(0)
// maybeNum1: Option[Int] = Some(value = 0)
maybeNum1.collect {
case 0 => -999
case n if n > 0 && n < 10 => n
}
// res78: Option[Int] = Some(value = -999)

val maybeNum2 = Option(1)
// maybeNum2: Option[Int] = Some(value = 1)
maybeNum2.collect {
case 0 => -999
case n if n > 0 && n < 10 => n
}
// res79: Option[Int] = Some(value = 1)

val maybeNum4 = Option(9)
// maybeNum4: Option[Int] = Some(value = 9)
maybeNum4.collect {
case 0 => -999
case n if n > 0 && n < 10 => n
}
// res80: Option[Int] = Some(value = 9)

val maybeNum3 = Option(10)
// maybeNum3: Option[Int] = Some(value = 10)
maybeNum3.collect {
case 0 => -999
case n if n > 0 && n < 10 => n
}
// res81: Option[Int] = None

val maybeNum5 = Option(-1)
// maybeNum5: Option[Int] = Some(value = -1)
maybeNum5.collect {
case 0 => -999
case n if n > 0 && n < 10 => n
}
// res82: Option[Int] = None

val maybeNum6 = Option.empty[Int]
// maybeNum6: Option[Int] = None
maybeNum6.collect {
case 0 => -999
case n if n > 0 && n < 10 => n
}
// res83: Option[Int] = None