// Conrad Cunningham's adaptation of the Option functions and other code
// from the "Error Handling" notes (chapter 4).

import scala.{Option => _, Either => _, _} // hide builtin

sealed trait Either[+E,+A] {

 def map[B](f: A => B): Either[E, B] = 
   this match {
     case Left(e)  => Left(e)
     case Right(a) => Right(f(a))
   }
   
 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]

import Either._

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)  // evaluate thunk
    catch { case e: Exception => Left(e) }

}
