Saturday, October 24, 2015

Functional programming in Scala, Chapter 4, Option type


object Example {

  sealed trait Option[+A] {
    def map[B](f: A => B): Option[B] = {
      this match {
        case None => None
        case Some(x) => Some(f(x))
      }
    }

    def flatMap[B](f: A => Option[B]): Option[B] = this match {
      case None => None
      case Some(x) => f(x) match {
        case None => None
        case y => y
      }
    }

    def getOrElse[B >: A](default: => B): B = {
      this match {
        case None => default
        case Some(x) => x
      }
    }

    def orElse[B >: A](ob: => Option[B]): Option[B] = {
      if (this == None) ob
      else this
    }

    def filter(f: A => Boolean): Option[A] = this match {
      case None => None
      case y@Some(x) => if (f(x)) y else None
    }
  }

  case class Some[+A](val get: A) extends Option[A]

  case object None extends Option[Nothing]

  def mean(xs: Seq[Double]): Option[Double] = {
    if (xs.isEmpty) None
    else Some(xs.sum / xs.length)
  }

  def variance(xs: Seq[Double]): Option[Double] =
    mean(xs).flatMap(
      m => mean(
        xs.map(x => {
          math.pow(x - m, 2)
        })))

  def lift[A, B](f: A => B): Option[A] => Option[B] = {
    _ map f
  }

  def map2[A, B, C](a: Option[A], b: Option[B])(f: (A, B) => C): Option[C] = {
    (a, b) match {
      case (None, _) => None
      case (_, None) => None
      case (Some(x), Some(y)) => Some(f(x, y))
    }
  }
  def map2a[A, B, C](a: Option[A], b: Option[B])(f: (A, B) => C): Option[C] = {
    a flatMap (
        aa => b map (
            bb => f(aa, bb)
          )
      )
  }
  def map2b[A, B, C](a: Option[A], b: Option[B])(f: (A, B) => C): Option[C] = {
    for {
      aa <- a
      bb <- b
    } yield f(aa, bb)
  }



  def sequence[A](a: List[Option[A]]): Option[List[A]] =
    a.foldRight[Option[List[A]]](Some(Nil))((x,y) => map2(x,y)(_ :: _))


  def parseInts(as: List[String]): Option[List[Int]] = {
    traverse(as)(a => Try(a.toInt))
  }

  def traverse1[A, B](as: List[A])(f: A => Option[B]): Option[List[B]] = {
    as match {
      case Nil => Some(Nil)
      case x :: xs => map2(f(x), traverse(as)(f))(_ :: _)
    }
  }
  def traverse[A, B](as: List[A])(f: A => Option[B]): Option[List[B]] = {
    as.foldRight[Option[List[B]]](Some(Nil))((h, t) => map2(f(h), t)(_ :: _))
  }



  def sequence2[A](a: List[Option[A]]): Option[List[A]] =
    traverse(a)(x => x)

  def positiveOption(x: Int): Option[Int] = {
    if(x > 0) Some(x) else None
  }

  def Try[A](a: => A): Option[A] =
    try Some(a)
    catch {
      case e: Exception => {
        println(e)
        None
      }
    }

  def parseInsuranceRateQuote(age: String, numberOfSpeedingTickets: String): Option[Double] = {
    val optAge: Option[Int] = Try(age.toInt)
    val optTickets: Option[Int] = Try(numberOfSpeedingTickets.toInt)
    map2(optAge, optTickets)(insuranceRateQuote)
  }

  def insuranceRateQuote(age: Int, numberOfSpeedingTickets: Int): Double = {
    age + (numberOfSpeedingTickets * 2)
  }
}


object Test {

  import Example._

  //  Some(1).map(_ + 1)
  //  Some(-1).filter(_ > 0)
  //  None.getOrElse(3)
  //  Some(4).getOrElse(5)
  //  None.orElse(Some(12))
  //  Some(Seq.empty[Double]).flatMap(mean(_))
  //  Some(Seq.empty[Double]).map(mean(_))

  lift(math.abs)(None)
  lift(math.abs)(Some(-3))

  traverse(List(1, 2, -3, -4))(positiveOption(_))
  traverse(List(-4, 1, 2, -3, -4))(positiveOption(_))
  traverse(List(1, 2, 3, 4))(positiveOption)
  sequence2(List(Some(1), Some(2)))
  sequence2(List(Some(1), Some(2), None))
  parseInts(List("5", "4", "3"))
  parseInts(List("5", "4", "3", "world"))
}

0 comments: