Option
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
.
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 Option
s like Option[Option[A]]
- Examples of nested
Option
s
// 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.
Check and Search
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]
isSome(a: A)
,- If the function returns
true
,filter
returns theSome(a: A)
again. - If the function returns
false
,filter
returnsNone
.
- If the function returns
- When
Option[A]
isNone
- It always returns
None
.
- It always returns
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