Skip to content

Instantly share code, notes, and snippets.

@kellydavid
Created February 20, 2020 10:55
Show Gist options
  • Save kellydavid/eadd339b72ed9a606b93437ac65e8632 to your computer and use it in GitHub Desktop.
Save kellydavid/eadd339b72ed9a606b93437ac65e8632 to your computer and use it in GitHub Desktop.
fpinscala_chapter04.scala
package com.dvdkly.fpinscala
import scala.annotation.tailrec
object Chapter04 {
/**
* Exercise 4.2
*/
object option {
sealed trait Option[+A] {
// apply f if the Option is not None
def map[B](f: A => B): Option[B] =
this match {
case Some(a) => Some(f(a))
case None => None
}
// apply f, which may fail, to the Option if not None
def flatMap[B](f: A => Option[B]): Option[B] = this.map(f).getOrElse(None)
// B >: A ie. B must be a supertype of A
def getOrElse[B >: A](default: => B): B =
this match {
case Some(a) => a
case None => default
}
// don't evaluate ob unless needed
//returns the first Option if it’s defined; otherwise, it returns the second Option.
def orElse[B >: A](ob: => Option[B]): Option[B] =
// val x: Option[Option[A]] = map(a => Some(a))
map(Some(_)) getOrElse ob
//convert Some to None, if the value doesn't satisfy f
def filter(f: A => Boolean): Option[A] = this.flatMap(a => if(f(a)) Some(a) else None)
}
case class Some[+A](get: A) extends Option[A]
case object None extends Option[Nothing]
object Option {
/**
* The map function lets us operate on values of type Option[A] using a function of type A => B,
* returning Option[B]. Another way of looking at this is that map turns a function f of type A => B into a
* function of type Option[A] => Option[B] .
*/
def lift[A,B](f: A => B): Option[A] => Option[B] = _ map f
val absO: Option[Double] => Option[Double] = lift(math.abs)
/**
* Exercise 4.3
*
* Write a generic function map2 that combines two Option values using a binary func- tion. If either Option
* value is None, then the return value is too.
*/
def map2[A, B,C](a: Option[A], b: Option[B])(f: (A, B) => C): Option[C] = a.flatMap(a => b.map(f(a, _)))
/**
* Exercise 4.4
* Write a function sequence that combines a list of Options into one Option containing a list of all the Some
* values in the original list. If the original list contains None even once, the result of the function should
* be None; otherwise the result should be Some with a list of all the values. Here is its signature:3
*/
def sequence[A](a: List[Option[A]]): Option[List[A]] =
a.foldRight[Option[List[A]]](Some(Nil))((opA, opB) => map2(opA, opB)(_ :: _))
def parseInts(a: List[String]): Option[List[Int]] = sequence(a map (i => `try`.Try(i.toInt)))
/**
* Exercise 4.5
* Implement this function. It’s straightforward to do using map and sequence, but try for a more efficient
* implementation that only looks at the list once. In fact, imple- ment sequence in terms of traverse.
*/
def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] =
a.foldRight[Option[List[B]]](Some(Nil))((a, acc) => map2(f(a), acc)(_ :: _))
def sequence2[A](a: List[Option[A]]): Option[List[A]] =
traverse(a)(x => x.)
// {
// @tailrec
// def loop(input: List[A], acc: List[B]): Option[List[B]] = {
// input match {
// case Nil => Some(acc)
// case x :: xs => f(x) match {
// case Some(b) => loop(xs, acc ++ List(b))
// case None => None
// }
// }
// }
// loop(a, List.empty)
// }
}
}
object testOption{
import option._
val x: Option[Int] = Some(33)
val y: Option[Int] = x.orElse(Some(42))
}
object employee {
import option._
case class Employee(name: String, department: String)
def lookupByName(name: String): Option[Employee] = ???
val joeDepartment: Option[String] = lookupByName("Joe").map(_.department)
}
object variance {
import option._
def mean(xs: Seq[Double]): Option[Double] =
if(xs.isEmpty) None
else Some(xs.sum / xs.length)
// math.pow(x - m, 2) where m is the mean
def variance(xs: Seq[Double]): Option[Double] =
mean(xs)
.map( m => xs.map(x => math.pow(x - m, 2)))
.flatMap(mean)
}
object `try` {
import option._
def Try[A](a: => A): Option[A] =
try Some(a)
catch { case e: Exception => None }
}
object insurance {
import option._
import `try`._
/**
* Top secret formula for computing an annual car
* insurance premium from two key factors.
*/
def insuranceRateQuote(age: Int, numberOfSpeedingTickets: Int): Double = ???
def parseInsuranceRateQuote( age: String, numberOfSpeedingTickets: String): Option[Double] = {
val optAge: Option[Int] = Try(age.toInt)
val optTickets: Option[Int] = Try(numberOfSpeedingTickets.toInt)
Option.map2(optAge, optTickets)(insuranceRateQuote)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment