4.1
Implement all of the preceding functions on Option. As you implement each function, try to think about what it means and in what situations you’d use it. We’ll explore when to use each of these functions next. Here are a few hints for solving this exercise:
- It’s fine to use pattern matching, though you should be able to implement all the functions besides map and getOrElse without resorting to pattern matching.
- For map and flatMap, the type signature should be enough to determine the implementation.
- getOrElse returns the result inside the Some case of the Option, or if the Option is None, returns the given default value.
- orElse returns the first Option if it’s defined; otherwise, it returns the second Option.
Solution
import scala.{Option => _, Some => _, Either => _, _} // hide std library `Option`, `Some` and `Either`, since we are writing our own in this chapter
sealed trait Option[+A] {
def map[B](f: A => B): Option[B] = this match {
case None => None
case Some(a) => Some(f(a))
}
def getOrElse[B>:A](default: => B): B = this match {
case None => default
case Some(a) => a
}
def flatMap[B](f: A => Option[B]): Option[B] = this match {
case None => None
case Some(a) => f(a)
}
def orElse[B>:A](ob: => Option[B]): Option[B] = this match {
case None => ob
case _ => this
}
def filter(f: A => Boolean): Option[A] = this match {
case Some(a) if f(a) => this
case _ => None
}
}
case class Some[+A](get: A) extends Option[A]
case object None extends Option[Nothing]
Run
// map
object Solution extends App {
val some: Option[Int] = Some(1)
println("Some(1) map (+10): " + some.map(_ + 10))
val none: Option[Int] = None
println("None map (+10): " + none.map(_ + 10))
}
// getOrElse
object Solution extends App {
val some: Option[Int] = Some(1)
println("Some(1) getOrElse (100): " + some.getOrElse(100))
val none: Option[Int] = None
println("None getOrElse (100): " + none.getOrElse(100))
}
// flatMap
object Solution extends App {
def f(a: Int): Option[Int] = Some(a * 10)
val some: Option[Int] = Some(4)
println("Some(4) flatMap (_ * 10): " + some.flatMap(f))
val none: Option[Int] = None
println("None flatMap (_ * 10): " + none.flatMap(f))
}
// orElse
object Solution extends App {
val some: Option[Int] = Some(4)
println("Some(4) orElse (40): " + some.orElse(Some(40)))
val none: Option[Int] = None
println("None orElse (40): " + none.orElse(Some(40)))
}
// filter
object Solution extends App {
def f(a: Int): Boolean = a > 100
val some400: Option[Int] = Some(400)
println("Some(400) filter (> 100): " + some400.filter(f))
val some40: Option[Int] = Some(40)
println("Some(40) filter (> 100): " + some40.filter(f))
val none: Option[Int] = None
println("None orElse (40): " + none.filter(f))
}
Output
// map
Some(1) map (+10): Some(11)
None map (+10): None
// getOrElse
Some(1) getOrElse (100): 1
None getOrElse (100): 100
// flatMap
Some(4) flatMap (_ * 10): Some(40)
None flatMap (_ * 10): None
// orElse
Some(4) orElse (40): Some(4)
None orElse (40): Some(40)
// filter
Some(400) filter (> 100): Some(400)
Some(40) filter (> 100): None
None orElse (40): None