Skip to main content

Option

Option

Use Constructor Methods

To create Option type value, instead of doing Some(value) or None, use data constructors provided by FP libraries like Scalaz and Cats.

import cats.syntax.option._

// No
Some(1)
// res0: Some[Int] = Some(value = 1)

// Yes
1.some
// res1: Option[Int] = Some(value = 1)

// No
None
// res2: None.type = None

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

Why?

As you can see above, Some(1) does not create an instance of Option[Int] type yet Some[Int].

List(Some(1), Some(2))
// res5: List[Some[Int]] = List(Some(value = 1), Some(value = 2))

List(None, None)
// res6: List[None.type] = List(None, None)

List(Some(1), None, Some(2))
// res7: List[Option[Int]] = List(Some(value = 1), None, Some(value = 2))
// What you want is List[Option[Int]]
import cats.syntax.option._

List(1.some, 2.some)
// res9: List[Option[Int]] = List(Some(value = 1), Some(value = 2))

List(none[Int], none[Int])
// res10: List[Option[Int]] = List(None, None)

List(1.some, none, 2.some)
// res11: List[Option[Int]] = List(Some(value = 1), None, Some(value = 2))
import cats._ // It is only for Applicative[F]

// No
def foo[F[_] : Applicative](n: Int): F[Option[Int]] = {
val r = Applicative[F].pure(Some(n))
r
}
// error: type mismatch;
// found : r.type (with underlying type F[Some[Int]])
// required: F[Option[Int]]
// Note: Some[Int] <: Option[Int], but type F is invariant in type _.
// You may wish to define _$$1 as +_$$1 instead. (SLS 4.5)
// r
// ^
import cats._
import cats.syntax.option._

// Yes
def foo[F[_] : Applicative](n: Int): F[Option[Int]] = {
val r = Applicative[F].pure(n.some)
r
}
import cats._

// No
def bar[F[_] : Applicative](n: Int): F[Option[Int]] = {
val r = Applicative[F].pure(None)
r
}
// error: type mismatch;
// found : r.type (with underlying type F[None.type])
// required: F[Option[Int]]
// Note: None.type <: Option[Int], but type F is invariant in type _.
// You may wish to define _$$3 as +_$$3 instead. (SLS 4.5)
// r
// ^
import cats._
import cats.syntax.option._

// Yes
def bar[F[_] : Applicative](n: Int): F[Option[Int]] = {
val r = Applicative[F].pure(none[Int])
r
}

No Get!

Never use Option.get to get the value in it as it may cause a NoSuchElementException if Option is None.

val n = None.get
// java.util.NoSuchElementException: None.get
// at scala.None$.get(Option.scala:627)
// at repl.MdocSession$MdocApp28$$anonfun$3.apply$mcV$sp(04-option.md:189)
// at repl.MdocSession$MdocApp28$$anonfun$3.apply(04-option.md:188)
// at repl.MdocSession$MdocApp28$$anonfun$3.apply(04-option.md:188)

There are three ways to get and use the value properly.

Pattern Matching

// import cats.syntax.option._ is only for .some and none.
import cats.syntax.option._

val maybeNum = 999.some
// maybeNum: Option[Int] = Some(value = 999)
maybeNum match {
case Some(n) =>
println(s"The number is $n")
case None =>
println("Error no number found")
}
// The number is 999

val maybeNum2 = none[Int]
// maybeNum2: Option[Int] = None
maybeNum2 match {
case Some(n) => n + 1
case None => 0
}
// res31: Int = 0

Catamorphism

Use fold. It's not so different from pattern matching.

// import cats.syntax.option._ is only for .some and none.
// fold is from Scala not Cats.
import cats.syntax.option._

// Option.fold(NoneCase)(n => do something with n)

val maybeNum = 999.some
// maybeNum: Option[Int] = Some(value = 999)
maybeNum.fold(0)(x => x + 1)
// res36: Int = 1000

val maybeNum2 = none[Int]
// maybeNum2: Option[Int] = None
maybeNum2.fold(0)(x => x + 1)
// res37: Int = 0

fold is exactly the same as doing map then getOrElse.

import cats.syntax.option._

val maybeNum = 999.some
// maybeNum: Option[Int] = Some(value = 999)
maybeNum.fold(0)(x => x + 1) // is equivalent to
// res39: Int = 1000 // is equivalent to
maybeNum.map(x => x + 1).getOrElse(0)
// res40: Int = 1000

val maybeNum2 = none[Int]
// maybeNum2: Option[Int] = None
maybeNum2.fold(0)(x => x + 1) // is equivalent to
// res41: Int = 0 // is equivalent to
maybeNum2.map(x => x + 1).getOrElse(0)
// res42: Int = 0

getOrElse

Or simply use getOrElse.

// import cats.syntax.option._ is only for .some and none.
// getOrElse is from Scala not Cats
import cats.syntax.option._

val maybeNum = 999.some
// maybeNum: Option[Int] = Some(value = 999)
maybeNum.map(x => x + 1).getOrElse(0)
// res44: Int = 1000

val maybeNum2 = none[Int]
// maybeNum2: Option[Int] = None
maybeNum2.map(x => x + 1).getOrElse(0)
// res45: Int = 0