/* Licensed under Apache-2.0 (C) All Contributors */
package io.github.dreamylost.examples.fb

/**
 * 第四章
 *
 * @author 梦境迷离
 * @version 1.0, 2019-05-01
 */
object errorhandling extends App {

  //    Console println Option.failingFn(1)
  Console println Option.failingFn2(1)
  Console println Option.mean(Seq(1, 2, 3, 4))
  Console println Either.safeDiv(1, 0)

  //可选数据类型
  //函数放到特质中只是风格上的选择。以便使用 obj fn arg1 方式替代fn(obj,arg1)
  sealed trait Option[+A] {

    //4.1：Option基本函数实现
    //如果Option不为None，对其应用f函数
    def map[B](f: A => B): Option[B] =
      this match {
        case None => None
        case Some(a) => Some(f(a))
      }

    //如果Option不为None，返回实际值，否则返回默认值，default: => B表示非立即求值
    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] = {
      map(f) getOrElse None
    }

    //使用模式匹配
    def flatMap_1[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 map (Some(_)) getOrElse ob
    }

    //使用模式匹配，B >: A 表示B的类型必须是A或者是A的超类类型
    def orElse_1[B >: A](ob: => Option[B]): Option[B] =
      this match {
        case None => ob
        case _ => this
      }

    def filter(f: A => Boolean): Option[A] = {
      flatMap(a => if (f(a)) Some(a) else None)
    }

    //使用模式匹配
    def filter_1(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]

  object Option {
    //书上例子
    def failingFn(i: Int): Int = {
      val y: Int = throw new Exception("fail!")
      try {
        val x = 42 + 5
        x + y
      } catch {
        case e: Exception => 43
      }
    }

    //书上例子
    def failingFn2(i: Int): Int = {
      try {
        val x = 42 + 5
        //抛出的异常可以被赋予任何类型
        x + ((throw new Exception("fail!")): Int)
      } catch {
        case e: Exception => 43
      }
    }

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

    /**
     * 4.2：根据flatMap实现方差函数
     *
     * @param xs
     * @return
     */
    def variance(xs: Seq[Double]): Option[Double] = {
      //方差：对每个元素求math.pow(x-m,2)的累加和并/元素个数，标准差再开2次根号
      mean(xs) flatMap (m => mean(xs.map(x => math.pow(x - m, 2))))
    }

    /**
     * 4.3：使用一个二元函数组合两个Option值
     *
     * @param a
     * @param b
     * @param f
     * @tparam A
     * @tparam B
     * @tparam C
     * @return
     */
    def map2[A, B, C](a: Option[A], b: Option[B])(f: (A, B) => C): Option[C] = {
      a flatMap { aa =>
        b map (bb => f(aa, bb))
      }
    }

    //for推导实现
    def map2_1[A, B, C](a: Option[A], b: Option[B])(f: (A, B) => C): Option[C] = {
      for {
        aa <- a
        bb <- b
      } yield f(aa, bb)
    }

    /**
     * 4.4：将Option列表结合为一个Option
     *
     * @param a
     * @tparam A
     * @return
     */
    def sequence[A](a: List[Option[A]]): Option[List[A]] = {
      a match {
        case Nil => Some(Nil)
        case h :: t => h flatMap (hh => sequence(t) map (hh :: _)) //显示递归
      }
    }

    //使用右折叠，参考3.15的concat函数
    def sequence_1[A](a: List[Option[A]]): Option[List[A]] = {
      a.foldRight[Option[List[A]]](Some(Nil))((x, y) => map2(x, y)(_ :: _))
    }

    //组合所有Option元素
    def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] = {
      a match {
        case Nil => Some(Nil)
        case h :: t => map2(f(h), traverse(t)(f))(_ :: _) //显示递归
      }
    }

    //使用右折叠
    def traverse_1[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] = {
      a.foldRight[Option[List[B]]](Some(Nil))((h, t) => map2(f(h), t)(_ :: _))
    }

    /**
     * 4.5：使用map和sequence函数，只遍历一次列表
     *
     * @param a
     * @tparam A
     * @return
     */
    def sequenceViaTraverse[A](a: List[Option[A]]): Option[List[A]] = {
      traverse(a)(x => x)
    }
  }

  //包含异常信息，基本与Option相似
  sealed trait Either[+E, +A] {

    //4.6：Either的map、flatMap、orElse、map2，类似Option
    def map[B](f: A => B): Either[E, B] = {
      this match {
        case Right(a) => Right(f(a))
        case Left(e) => Left(e)
      }
    }

    //对右侧进行mapping时，必须限定左边的类型参数是E的父类型
    def flatMap[EE >: E, B](f: A => Either[EE, B]): Either[EE, B] = {
      this match {
        case Left(e) => Left(e)
        case Right(a) => f(a)
      }
    }

    def orElse[EE >: E, AA >: A](b: => Either[EE, AA]): Either[EE, AA] = {
      this match {
        case Left(_) => b
        case Right(a) => Right(a)
      }
    }

    def map2[EE >: E, B, C](b: Either[EE, B])(f: (A, B) => C): Either[EE, C] = {
      for { a <- this; b1 <- b } yield f(a, b1)
    }
  }

  //互斥并集。一般表示失败
  case class Left[+E](get: E) extends Either[E, Nothing]

  //一般表示成功
  case class Right[+A](get: A) extends Either[Nothing, A]

  object Either {

    //书上例子
    def mean(xs: IndexedSeq[Double]): Either[String, Double] = {
      if (xs.isEmpty)
        Left("mean of empty list!")
      else
        Right(xs.sum / xs.length)
    }

    //书上例子
    def safeDiv(x: Int, y: Int): Either[Exception, Int] = {
      try Right(x / y)
      catch {
        case e: Exception => Left(e)
      }
    }

    //书上例子
    def Try[A](a: => A): Either[Exception, A] = {
      try Right(a)
      catch {
        case e: Exception => Left(e)
      }
    }

    //4.7：类似Option
    def traverse[E, A, B](es: List[A])(f: A => Either[E, B]): Either[E, List[B]] = {
      es match {
        case Nil => Right(Nil)
        case h :: t => (f(h) map2 traverse(t)(f))(_ :: _)
      }
    }

    def traverse_1[E, A, B](es: List[A])(f: A => Either[E, B]): Either[E, List[B]] = {
      es.foldRight[Either[E, List[B]]](Right(Nil))((a, b) => f(a).map2(b)(_ :: _))
    }

    def sequence[E, A](es: List[Either[E, A]]): Either[E, List[A]] = {
      traverse(es)(x => x)
    }
  }

  //4.8使用Either[List[E],_]
  //或使用新数据结构
  trait Partial[+A, +B]

  case class Errors[+A](get: Seq[A]) extends Partial[A, Nothing]

  case class Success[+B](get: B) extends Partial[Nothing, B]

}
