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))
- Cats
- Scalaz
// 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))
// What you want is List[Option[Int]]
import scalaz._, Scalaz._
List(1.some, 2.some)
// res13: List[Option[Int]] = List(Some(value = 1), Some(value = 2))
List(none[Int], none[Int])
// res14: List[Option[Int]] = List(None, None)
List(1.some, none, 2.some)
// res15: List[Option[Int]] = List(Some(value = 1), None, Some(value = 2))
- Cats
- Scalaz
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 scalaz._, Scalaz._ // 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 _$$2 as +_$$2 instead. (SLS 4.5)
// r
// ^
import scalaz._
import Scalaz._
// Yes
def foo[F[_] : Applicative](n: Int): F[Option[Int]] = {
val r = Applicative[F].pure(n.some)
r
}
- Cats
- Scalaz
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
}
import scalaz._, Scalaz._
// 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 _$$4 as +_$$4 instead. (SLS 4.5)
// r
// ^
import scalaz._
import Scalaz._
// 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
- Cats
- Scalaz
// 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
// importing Scalaz is only for .some and none.
import scalaz._, Scalaz._
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
}
// res34: 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
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
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