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 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.
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,filterreturns theSome(a: A)again. - If the function returns
false,filterreturnsNone.
- 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