diff options
author | Geoffrey Washburn <geoffrey.washburn@epfl.ch> | 2008-04-18 10:01:28 +0000 |
---|---|---|
committer | Geoffrey Washburn <geoffrey.washburn@epfl.ch> | 2008-04-18 10:01:28 +0000 |
commit | 8ebd73e6d786f11a195fc53d18f9f230eb89a2c7 (patch) | |
tree | ec0b13f185417e1338de951c8e92d49186c24fd4 /src | |
parent | 07567a3ff9b7a160c0cfec525e6c21117ec70aaa (diff) | |
download | scala-8ebd73e6d786f11a195fc53d18f9f230eb89a2c7.tar.gz scala-8ebd73e6d786f11a195fc53d18f9f230eb89a2c7.tar.bz2 scala-8ebd73e6d786f11a195fc53d18f9f230eb89a2c7.zip |
Applied the patch for Either after some revisions.
Diffstat (limited to 'src')
-rw-r--r-- | src/library/scala/Either.scala | 303 | ||||
-rw-r--r-- | src/library/scala/List.scala | 43 | ||||
-rw-r--r-- | src/library/scala/Option.scala | 14 |
3 files changed, 358 insertions, 2 deletions
diff --git a/src/library/scala/Either.scala b/src/library/scala/Either.scala new file mode 100644 index 0000000000..990141a883 --- /dev/null +++ b/src/library/scala/Either.scala @@ -0,0 +1,303 @@ +package scala + +/** + * <p> + * The <code>Either</code> type represents a value of one of two possible types (a disjoint union). + * The data constructors; <code>Left</code> and <code>Right</code> represent the two possible + * values. The <code>Either</code> type is often used as an alternative to + * <code>scala.Option</code> where <code>Left</code> represents failure (by convention) and + * <code>Right</code> is akin to <code>Some</code>. + * </p> + * + * @author <a href="mailto:research@workingmouse.com">Tony Morris</a>, Workingmouse + * @version 1.0, 11/10/2008 + */ +sealed trait Either[+A, +B] { + /** + * Projects this <code>Either</code> as a <code>Left</code>. + */ + lazy val left = Either.LeftProjection(this) + + /** + * Projects this <code>Either</code> as a <code>Right</code>. + */ + lazy val right = Either.RightProjection(this) + + /** + * Deconstruction of the <code>Either</code> type (in contrast to pattern matching). + */ + 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 <code>Left</code>, then return the left value in <code>Right</code> or vice versa. + */ + lazy val swap = this match { + case Left(a) => Right(a) + case Right(b) => Left(b) + } + + /** + * Returns <code>true</code> if this is a <code>Left</code>, <code>false</code> otherwise. + */ + lazy val isLeft = this match { + case Left(_) => true + case Right(_) => false + } + + /** + * Returns <code>true</code> if this is a <code>Right</code>, <code>false</code> otherwise. + */ + lazy val isRight = this match { + case Left(_) => false + case Right(_) => true + } +} +/** + * The left side of the disjoint union, as opposed to the <code>Right</code> 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] +/** + * The right side of the disjoint union, as opposed to the <code>Left</code> 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] + +object Either { + /** + * Projects an <code>Either</code> into a <code>Left</code>. + * + * @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 <code>Left</code> or throws <code>Predef.NoSuchElementException</code> + * if this is a <code>Right</code>. + * + * @throws Predef.NoSuchElementException if the option is empty. + */ + lazy val get = e match { + case Left(a) => a + case Right(_) => throw new NoSuchElementException("Either.left.value on Right") + } + + /** + * Executes the given side-effect if this is a <code>Left</code>. + * + * @param e The side-effect to execute. + */ + def foreach(f: A => Unit) = e match { + case Left(a) => f(a) + case Right(_) => {} + } + + /** + * Returns the value from this <code>Left</code> or the given argument if this is a + * <code>Right</code>. + */ + def getOrElse[AA >: A](or: => AA) = e match { + case Left(a) => a + case Right(_) => or + } + + /** + * Returns <code>true</code> if <code>Right</code> or returns the result of the application of + * the given function to the <code>Left</code> value. + */ + def forall(f: A => Boolean) = e match { + case Left(a) => f(a) + case Right(_) => true + } + + /** + * Returns <code>false</code> if <code>Right</code> or returns the result of the application of + * the given function to the <code>Left</code> value. + */ + def exists(f: A => Boolean) = e match { + case Left(a) => f(a) + case Right(_) => false + } + + /** + * Binds the given function across <code>Left</code>. + * + * @param The function to bind across <code>Left</code>. + */ + 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 <code>Left</code>. + */ + def map[X](f: A => X) = e match { + case Left(a) => Left(f(a)) + case Right(b) => Right(b) + } + + /** + * Returns <code>None</code> if this is a <code>Right</code> or if the given predicate + * <code>p</code> does not hold for the left value, otherwise, returns a <code>Left</code>. + */ + 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 <code>Seq</code> containing the <code>Left</code> value if it exists or an empty + * <code>Seq</code> if this is a <code>Right</code>. + */ + lazy val toSeq = e match { + case Left(a) => Seq.singleton(a) + case Right(_) => Seq.empty + } + + /** + * Returns a <code>Some</code> containing the <code>Left</code> value if it exists or a + * <code>None</code> if this is a <code>Right</code>. + */ + lazy val toOption = e match { + case Left(a) => Some(a) + case Right(_) => None + } + } + + /** + * Projects an <code>Either</code> into a <code>Right</code>. + * + * @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 <code>Right</code> or throws + * <code>Predef.NoSuchElementException</code> if this is a <code>Left</code>. + * + * @throws Predef.NoSuchElementException if the projection is <code>Left</code>. + */ + lazy val get = e match { + case Left(_) => throw new NoSuchElementException("Either.right.value on Left") + case Right(a) => a + } + + /** + * Executes the given side-effect if this is a <code>Right</code>. + * + * @param e The side-effect to execute. + */ + def foreach(f: B => Unit) = e match { + case Left(_) => {} + case Right(b) => f(b) + } + + /** + * Returns the value from this <code>Right</code> or the given argument if this is a + * <code>Left</code>. + */ + def getOrElse[BB >: B](or: => BB) = e match { + case Left(_) => or + case Right(b) => b + } + + /** + * Returns <code>true</code> if <code>Left</code> or returns the result of the application of + * the given function to the <code>Right</code> value. + */ + def forall(f: B => Boolean) = e match { + case Left(_) => true + case Right(b) => f(b) + } + + /** + * Returns <code>false</code> if <code>Left</code> or returns the result of the application of + * the given function to the <code>Right</code> value. + */ + def exists(f: B => Boolean) = e match { + case Left(_) => false + case Right(b) => f(b) + } + + /** + * Binds the given function across <code>Right</code>. + * + * @param The function to bind across <code>Right</code>. + */ + def flatMap[AA >: A, Y](f: B => Either[AA, Y]) = e match { + case Left(a) => Left(a) + case Right(b) => f(b) + } + + /** + * Maps the function argument through <code>Right</code>. + */ + def map[Y](f: B => Y) = e match { + case Left(a) => Left(a) + case Right(b) => Right(f(b)) + } + + /** + * Returns <code>None</code> if this is a <code>Left</code> or if the given predicate + * <code>p</code> does not hold for the right value, otherwise, returns a <code>Right</code>. + */ + 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 <code>Seq</code> containing the <code>Right</code> value if it exists or an empty + * <code>Seq</code> if this is a <code>Left</code>. + */ + lazy val toSeq = e match { + case Left(_) => Seq.empty + case Right(b) => Seq.singleton(b) + } + + /** + * Returns a <code>Some</code> containing the <code>Right</code> value if it exists or a + * <code>None</code> if this is a <code>Left</code>. + */ + lazy val toOption = e match { + case Left(_) => None + case Right(b) => Some(b) + } + } + + /** + * Joins an <code>Either</code> through <code>Left</code>. + */ + def joinLeft[A, B](es: Either[Either[A, B], B]) = + es.left.flatMap(x => x) + + /** + * Joins an <code>Either</code> through <code>Right</code>. + */ + def joinRight[A, B](es: Either[A, Either[A, B]]) = + es.right.flatMap(x => x) + + /** + * Takes an <code>Either</code> to its contained value within <code>Left</code> or + * <code>Right</code>. + */ + def merge[T](e: Either[T, T]) = e match { + case Left(t) => t + case Right(t) => t + } + + /** + * If the condition satisfies, return the given A in <code>Left</code>, otherwise, return the + * given B in <code>Right</code>. + */ + def cond[A, B](test: Boolean, right: => B, left: => A) = + if(test) Right(right) else Left(left) +} diff --git a/src/library/scala/List.scala b/src/library/scala/List.scala index f87b034cde..6f4b52b4fd 100644 --- a/src/library/scala/List.scala +++ b/src/library/scala/List.scala @@ -145,10 +145,10 @@ object List { b.toList } - /** Transforms a list of pair into a pair of lists. + /** Transforms a list of pairs into a pair of lists. * * @param xs the list of pairs to unzip - * @return a pair of lists: the first list in the pair contains the list + * @return a pair of lists. */ def unzip[A,B](xs: List[(A,B)]): (List[A], List[B]) = { val b1 = new ListBuffer[A] @@ -162,6 +162,45 @@ object List { (b1.toList, b2.toList) } + /** Transforms an iterable of pairs into a pair of lists. + * + * @param xs the iterable of pairs to unzip + * @return a pair of lists. + */ + def unzip[A,B](xs: Iterable[(A,B)]): (List[A], List[B]) = + xs.foldRight[(List[A], List[B])]((Nil, Nil)) { + case ((x, y), (xs, ys)) => (x :: xs, y :: ys) + } + + /** + * Returns the <code>Left</code> values in the given <code>Iterable</code> of <code>Either</code>s. + */ + def lefts[A, B](es: Iterable[Either[A, B]]) = + es.foldRight[List[A]](Nil)((e, as) => e match { + case Left(a) => a :: as + case Right(_) => as + }) + + /** + * Returns the <code>Right</code> values in the given<code>Iterable</code> of <code>Either</code>s. + */ + def rights[A, B](es: Iterable[Either[A, B]]) = + es.foldRight[List[B]](Nil)((e, bs) => e match { + case Left(_) => bs + case Right(b) => b :: bs + }) + + /** Transforms an Iterable of Eithers into a pair of lists. + * + * @param xs the iterable of Eithers to separate + * @return a pair of lists. + */ + def separate[A,B](es: Iterable[Either[A,B]]): (List[A], List[B]) = + es.foldRight[(List[A], List[B])]((Nil, Nil)) { + case (Left(a), (lefts, rights)) => (a :: lefts, rights) + case (Right(b), (lefts, rights)) => (lefts, b :: rights) + } + /** Converts an iterator to a list. * * @param it the iterator to convert diff --git a/src/library/scala/Option.scala b/src/library/scala/Option.scala index 8652386975..b24b575216 100644 --- a/src/library/scala/Option.scala +++ b/src/library/scala/Option.scala @@ -112,6 +112,20 @@ sealed abstract class Option[+A] extends Product { */ def toList: List[A] = if (isEmpty) List() else List(this.get) + + /** An <code>Either</code> that is a <code>Left</code> with the given argument + * <code>left</code> if this is empty, or a <code>Right</code> if this is nonempty with the + * option's value. + */ + def toRight[X](left: => X) = + if (isEmpty) Left(left) else Right(this.get) + + /** An <code>Either</code> that is a <code>Right</code> with the given argument + * <code>right</code> if this is empty, or a <code>Left</code> if this is nonempty with the + * option's value. + */ + def toLeft[X](right: => X) = + if (isEmpty) Right(right) else Left(this.get) } /** Class <code>Some[A]</code> represents existing values of type |