/* __ *\
** ________ ___ / / ___ Scala API **
** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL **
** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
** /____/\___/_/ |_/____/_/ | | **
** |/ **
\* */
package scala
package util
import scala.language.implicitConversions
/** Represents a value of one of two possible types (a disjoint union.)
* Instances of Either are either an instance of [[scala.util.Left]] or [[scala.util.Right]].
*
* A common use of Either is as an alternative to [[scala.Option]] for dealing
* with possible missing values. In this usage, [[scala.None]] is replaced
* with a [[scala.util.Left]] which can contain useful information.
* [[scala.util.Right]] takes the place of [[scala.Some]]. Convention dictates
* that Left is used for failure and Right is used for success.
*
* For example, you could use `Either[String, Int]` to detect whether a
* received input is a String or an Int.
*
* {{{
* val in = Console.readLine("Type Either a string or an Int: ")
* val result: Either[String,Int] = try {
* Right(in.toInt)
* } catch {
* case e: Exception =>
* Left(in)
* }
*
* println( result match {
* case Right(x) => "You passed me the Int: " + x + ", which I will increment. " + x + " + 1 = " + (x+1)
* case Left(x) => "You passed me the String: " + x
* })
* }}}
*
* A ''projection'' can be used to selectively operate on a value of type Either,
* depending on whether it is of type Left or Right. For example, to transform an
* Either using a function, in the case where it's a Left, one can first apply
* the `left` projection and invoke `map` on that projected Either. If a `right`
* projection is applied to that Left, the original Left is returned, unmodified.
*
* {{{
* val l: Either[String, Int] = Left("flower")
* val r: Either[String, Int] = Right(12)
* l.left.map(_.size): Either[Int, Int] // Left(6)
* r.left.map(_.size): Either[Int, Int] // Right(12)
* l.right.map(_.toDouble): Either[String, Double] // Left("flower")
* r.right.map(_.toDouble): Either[String, Double] // Right(12.0)
* }}}
*
* Like with other types which define a `map` method, the same can be achieved
* using a for-comprehension:
* {{{
* for (s <- l.left) yield s.size // Left(6)
* }}}
*
* To support multiple projections as generators in for-comprehensions, the Either
* type also defines a `flatMap` method.
*
* @author <a href="mailto:research@workingmouse.com">Tony Morris</a>, Workingmouse
* @version 1.0, 11/10/2008
* @since 2.7
*/
sealed abstract class Either [ +A , +B ] extends Product with Serializable {
/**
* Projects this `Either` as a `Left`.
*/
def left = Either . LeftProjection ( this )
/**
* Projects this `Either` as a `Right`.
*/
def right = Either . RightProjection ( this )
/**
* Applies `fa` if this is a `Left` or `fb` if this is a `Right`.
*
* @example {{{
* val result: Either[Exception, Value] = possiblyFailingOperation()
* log(result.fold(
* ex => "Operation failed with " + ex,
* v => "Operation produced value: " + v
* ))
* }}}
*
* @param fa the function to apply if this is a `Left`
* @param fb the function to apply if this is a `Right`
* @return the results of applying the function
*/
def fold [ X ]( fa : A => X , fb : B => X ) = this match {
case Left ( a ) => fa ( a )
case Right ( b ) => fb ( b )
}
/**
* If this is a `Left`, then return the left value in `Right` or vice versa.
*
* @example {{{
* val l: Either[String, Int] = Left("left")
* val r: Either[Int, String] = l.swap // Result: Right("left")
* }}}
*/
def swap = this match {
case Left ( a ) => Right ( a )
case Right ( b ) => Left ( b )
}
/**
* Joins an `Either` through `Right`.
*
* This method requires that the right side of this Either is itself an
* Either type. That is, this must be some type like: {{{
* Either[A, Either[A, C]]
* }}} (which respects the type parameter bounds, shown below.)
*
* If this instance is a Right[Either[A, C]] then the contained Either[A, C]
* will be returned, otherwise this value will be returned unmodified.
*
* @example {{{
* Right[String, Either[String, Int]](Right(12)).joinRight // Result: Right(12)
* Right[String, Either[String, Int]](Left("flower")).joinRight // Result: Left("flower")
* Left[String, Either[String, Int]]("flower").joinRight // Result: Left("flower")
* }}}
*
* This method, and `joinLeft`, are analogous to `Option#flatten`
*/
def joinRight [ A1 >: A , B1 >: B , C ]( implicit ev : B1 <: < Either [ A1 , C ]) : Either [ A1 , C ] = this match {
case Left ( a ) => Left ( a )
case Right ( b ) => b
}
/**
* Joins an `Either` through `Left`.
*
* This method requires that the left side of this Either is itself an
* Either type. That is, this must be some type like: {{{
* Either[Either[C, B], B]
* }}} (which respects the type parameter bounds, shown below.)
*
* If this instance is a Left[Either[C, B]] then the contained Either[C, B]
* will be returned, otherwise this value will be returned unmodified.
*
* {{{
* Left[Either[Int, String], String](Right("flower")).joinLeft // Result: Right("flower")
* Left[Either[Int, String], String](Left(12)).joinLeft // Result: Left(12)
* Right[Either[Int, String], String]("daisy").joinLeft // Result: Right("daisy")
* }}}
*
* This method, and `joinRight`, are analogous to `Option#flatten`
*/
def joinLeft [ A1 >: A , B1 >: B , C ]( implicit ev : A1 <: < Either [ C , B1 ]) : Either [ C , B1 ] = this match {
case Left ( a ) => a
case Right ( b ) => Right ( b )
}
/**
* Returns `true` if this is a `Left`, `false` otherwise.
*
* {{{
* Left("tulip").isLeft // true
* Right("venus fly-trap").isLeft // false
* }}}
*/
def isLeft : Boolean
/**
* Returns `true` if this is a `Right`, `false` otherwise.
*
* {{{
* Left("tulip").isRight // false
* Right("venus fly-trap").isRight // true
* }}}
*/
def isRight : Boolean
}
/**
* The left side of the disjoint union, as opposed to the [[scala.util.Right]] side.
*
* @author <a href="mailto:research@workingmouse.com">Tony Morris</a>, Workingmouse
* @version 1.0, 11/10/2008
*/
final case class Left [ +A , +B ]( a : A ) extends Either [ A , B ] {
def isLeft = true
def isRight = false
}
/**
* The right side of the disjoint union, as opposed to the [[scala.util.Left]] side.
*
* @author <a href="mailto:research@workingmouse.com">Tony Morris</a>, Workingmouse
* @version 1.0, 11/10/2008
*/
final case class Right [ +A , +B ]( b : B ) extends Either [ A , B ] {
def isLeft = false
def isRight = true
}
object Either {
/**
* Allows use of a `merge` method to extract values from Either instances
* regardless of whether they are Left or Right.
*
* {{{
* val l = Left(List(1)): Either[List[Int], Vector[Int]]
* val r = Right(Vector(1)): Either[List[Int], Vector[Int]]
* l.merge: Seq[Int] // List(1)
* r.merge: Seq[Int] // Vector(1)
* }}}
*/
implicit class MergeableEither [ A ]( private val x : Either [ A , A ]) extends AnyVal {
def merge : A = x match {
case Left ( a ) => a
case Right ( a ) => a
}
}
/**
* Projects an `Either` into a `Left`.
*
* This allows for-comprehensions over Either instances - for example {{{
* for (s <- Left("flower").left) yield s.length // Left(6)
* }}}
*
* Continuing the analogy with [[scala.Option]], a `LeftProjection` declares
* that `Left` should be analogous to `Some` in some code.
*
* {{{
* // using Option:
* def interactWithDB(x: Query): Option[Result] =
* try {
* Some(getResultFromDatabase(x))
* } catch {
* case ex => None
* }
*
* // this will only be executed if interactWithDB returns a Some
* val report =
* for (r <- interactWithDB(someQuery)) yield generateReport(r)
* if (report.isDefined)
* send(report)
* else
* log("report not generated, not sure why...")
* }}}
*
* {{{
* // using Either
* def interactWithDB(x: Query): Either[Exception, Result] =
* try {
* Right(getResultFromDatabase(x))
* } catch {
* case ex => Left(ex)
* }
*
* // this will only be executed if interactWithDB returns a Right
* val report =
* for (r <- interactWithDB(someQuery).right) yield generateReport(r)
* if (report.isRight)
* send(report)
* else
* log("report not generated, reason was " + report.left.get)
* }}}
*
* @author <a href="mailto:research@workingmouse.com">Tony Morris</a>, Workingmouse
* @version 1.0, 11/10/2008
*/
final case class LeftProjection [ +A , +B ]( e : Either [ A , B ]) {
/**
* Returns the value from this `Left` or throws `java.util.NoSuchElementException`
* if this is a `Right`.
*
* {{{
* Left(12).left.get // 12
* Right(12).left.get // NoSuchElementException
* }}}
*
* @throws java.util.NoSuchElementException if the projection is [[scala.util.Right]]
*/
def get = e match {
case Left ( a ) => a
case Right ( _ ) => throw new NoSuchElementException ( "Either.left.value on Right" )
}
/**
* Executes the given side-effecting function if this is a `Left`.
*
* {{{
* Left(12).left.foreach(x => println(x)) // prints "12"
* Right(12).left.foreach(x => println(x)) // doesn't print
* }}}
* @param f The side-effecting function to execute.
*/
def foreach [ U ]( f : A => U ) = e match {
case Left ( a ) => f ( a )
case Right ( _ ) => {}
}
/**
* Returns the value from this `Left` or the given argument if this is a
* `Right`.
*
* {{{
* Left(12).left.getOrElse(17) // 12
* Right(12).left.getOrElse(17) // 17
* }}}
*
*/
def getOrElse [ AA >: A ]( or : => AA ) = e match {
case Left ( a ) => a
case Right ( _ ) => or
}
/**
* Returns `true` if `Right` or returns the result of the application of
* the given function to the `Left` value.
*
* {{{
* Left(12).left.forall(_ > 10) // true
* Left(7).left.forall(_ > 10) // false
* Right(12).left.forall(_ > 10) // true
* }}}
*
*/
def forall ( @deprecatedName ( 'f) p : A => Boolean ) = e match {
case Left ( a ) => p ( a )
case Right ( _ ) => true
}
/**
* Returns `false` if `Right` or returns the result of the application of
* the given function to the `Left` value.
*
* {{{
* Left(12).left.exists(_ > 10) // true
* Left(7).left.exists(_ > 10) // false
* Right(12).left.exists(_ > 10) // false
* }}}
*
*/
def exists ( @deprecatedName ( 'f) p : A => Boolean ) = e match {
case Left ( a ) => p ( a )
case Right ( _ ) => false
}
/**
* Binds the given function across `Left`.
*
* {{{
* Left(12).left.flatMap(x => Left("scala")) // Left("scala")
* Right(12).left.flatMap(x => Left("scala") // Right(12)
* }}}
* @param f The function to bind across `Left`.
*/
def flatMap [ BB >: B , X ]( f : A => Either [ X , BB ]) = e match {
case Left ( a ) => f ( a )
case Right ( b ) => Right ( b )
}
/**
* Maps the function argument through `Left`.
*
* {{{
* Left(12).left.map(_ + 2) // Left(14)
* Right[Int, Int](12).left.map(_ + 2) // Right(12)
* }}}
*/
def map [ X ]( f : A => X ) = e match {
case Left ( a ) => Left ( f ( a ))
case Right ( b ) => Right ( b )
}
/**
* Returns `None` if this is a `Right` or if the given predicate
* `p` does not hold for the left value, otherwise, returns a `Left`.
*
* {{{
* Left(12).left.filter(_ > 10) // Some(Left(12))
* Left(7).left.filter(_ > 10) // None
* Right(12).left.filter(_ > 10) // None
* }}}
*/
def filter [ Y ]( p : A => Boolean ) : Option [ Either [ A , Y ]] = e match {
case Left ( a ) => if ( p ( a )) Some ( Left ( a )) else None
case Right ( b ) => None
}
/**
* Returns a `Seq` containing the `Left` value if it exists or an empty
* `Seq` if this is a `Right`.
*
* {{{
* Left(12).left.toSeq // Seq(12)
* Right(12).left.toSeq // Seq()
* }}}
*/
def toSeq = e match {
case Left ( a ) => Seq ( a )
case Right ( _ ) => Seq . empty
}
/**
* Returns a `Some` containing the `Left` value if it exists or a
* `None` if this is a `Right`.
*
* {{{
* Left(12).left.toOption // Some(12)
* Right(12).left.toOption // None
* }}}
*/
def toOption = e match {
case Left ( a ) => Some ( a )
case Right ( _ ) => None
}
}
/**
* Projects an `Either` into a `Right`.
*
* This allows for-comprehensions over Either instances - for example {{{
* for (s <- Right("flower").right) yield s.length // Right(6)
* }}}
*
* Continuing the analogy with [[scala.Option]], a `RightProjection` declares
* that `Right` should be analogous to `Some` in some code.
*
* Analogous to `LeftProjection`, see example usage in its documentation above.
*
* @author <a href="mailto:research@workingmouse.com">Tony Morris</a>, Workingmouse
* @version 1.0, 11/10/2008
*/
final case class RightProjection [ +A , +B ]( e : Either [ A , B ]) {
/**
* Returns the value from this `Right` or throws
* `java.util.NoSuchElementException` if this is a `Left`.
*
* {{{
* Right(12).right.get // 12
* Left(12).right.get // NoSuchElementException
* }}}
*
* @throws java.util.NoSuchElementException if the projection is `Left`.
*/
def get = e match {
case Left ( _ ) => throw new NoSuchElementException ( "Either.right.value on Left" )
case Right ( a ) => a
}
/**
* Executes the given side-effecting function if this is a `Right`.
*
* {{{
* Right(12).right.foreach(x => println(x)) // prints "12"
* Left(12).right.foreach(x => println(x)) // doesn't print
* }}}
* @param f The side-effecting function to execute.
*/
def foreach [ U ]( f : B => U ) = e match {
case Left ( _ ) => {}
case Right ( b ) => f ( b )
}
/**
* Returns the value from this `Right` or the given argument if this is a
* `Left`.
*
* {{{
* Right(12).right.getOrElse(17) // 12
* Left(12).right.getOrElse(17) // 17
* }}}
*/
def getOrElse [ BB >: B ]( or : => BB ) = e match {
case Left ( _ ) => or
case Right ( b ) => b
}
/**
* Returns `true` if `Left` or returns the result of the application of
* the given function to the `Right` value.
*
* {{{
* Right(12).right.forall(_ > 10) // true
* Right(7).right.forall(_ > 10) // false
* Left(12).right.forall(_ > 10) // true
* }}}
*/
def forall ( f : B => Boolean ) = e match {
case Left ( _ ) => true
case Right ( b ) => f ( b )
}
/**
* Returns `false` if `Left` or returns the result of the application of
* the given function to the `Right` value.
*
* {{{
* Right(12).right.exists(_ > 10) // true
* Right(7).right.exists(_ > 10) // false
* Left(12).right.exists(_ > 10) // false
* }}}
*/
def exists ( @deprecatedName ( 'f) p : B => Boolean ) = e match {
case Left ( _ ) => false
case Right ( b ) => p ( b )
}
/**
* Binds the given function across `Right`.
*
* @param f The function to bind across `Right`.
*/
def flatMap [ AA >: A , Y ]( f : B => Either [ AA , Y ]) = e match {
case Left ( a ) => Left ( a )
case Right ( b ) => f ( b )
}
/**
* The given function is applied if this is a `Right`.
*
* {{{
* Right(12).right.map(x => "flower") // Result: Right("flower")
* Left(12).right.map(x => "flower") // Result: Left(12)
* }}}
*/
def map [ Y ]( f : B => Y ) = e match {
case Left ( a ) => Left ( a )
case Right ( b ) => Right ( f ( b ))
}
/** Returns `None` if this is a `Left` or if the
* given predicate `p` does not hold for the right value,
* otherwise, returns a `Right`.
*
* {{{
* Right(12).right.filter(_ > 10) // Some(Right(12))
* Right(7).right.filter(_ > 10) // None
* Left(12).right.filter(_ > 10) // None
* }}}
*/
def filter [ X ]( p : B => Boolean ) : Option [ Either [ X , B ]] = e match {
case Left ( _ ) => None
case Right ( b ) => if ( p ( b )) Some ( Right ( b )) else None
}
/** Returns a `Seq` containing the `Right` value if
* it exists or an empty `Seq` if this is a `Left`.
*
* {{{
* Right(12).right.toSeq // Seq(12)
* Left(12).right.toSeq // Seq()
* }}}
*/
def toSeq = e match {
case Left ( _ ) => Seq . empty
case Right ( b ) => Seq ( b )
}
/** Returns a `Some` containing the `Right` value
* if it exists or a `None` if this is a `Left`.
*
* {{{
* Right(12).right.toOption // Some(12)
* Left(12).right.toOption // None
* }}}
*/
def toOption = e match {
case Left ( _ ) => None
case Right ( b ) => Some ( b )
}
}
/** If the condition is satisfied, return the given `B` in `Right`,
* otherwise, return the given `A` in `Left`.
*
* {{{
* val userInput: String = ...
* Either.cond(
* userInput.forall(_.isDigit) && userInput.size == 10,
* PhoneNumber(userInput),
* "The input (%s) does not look like a phone number".format(userInput)
* }}}
*/
def cond [ A , B ]( test : Boolean , right : => B , left : => A ) : Either [ A , B ] =
if ( test ) Right ( right ) else Left ( left )
}