From b42e1f1902c47f4b5fef0be17f5a5c606ac6a777 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Sun, 1 Nov 2009 17:29:19 +0000 Subject: Some structural improvements to Either and Opti... Some structural improvements to Either and Option which leverage recent awesomeness in constraining type parameters. In Either I was able to define joinLeft and joinRight on the instance rather than on the object, and while I didn't manage that directly with merge, it can at least be accomplished via implicit as endorsed by martin 25/Jun/09 on scala-internals. --- src/library/scala/Either.scala | 113 +++++++++++++++++++++++++---------------- src/library/scala/Option.scala | 20 +++----- 2 files changed, 77 insertions(+), 56 deletions(-) diff --git a/src/library/scala/Either.scala b/src/library/scala/Either.scala index 77db581526..5e1d231272 100644 --- a/src/library/scala/Either.scala +++ b/src/library/scala/Either.scala @@ -54,15 +54,31 @@ sealed abstract class Either[+A, +B] { case Right(b) => Left(b) } + /** + * Joins an Either through Right. + */ + 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. + */ + 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. */ - def isLeft = false // Default here, overriden in Left + def isLeft: Boolean /** * Returns true if this is a Right, false otherwise. */ - def isRight = false // Default here, overriden in Right. + def isRight: Boolean } /** @@ -71,7 +87,10 @@ sealed abstract class Either[+A, +B] { * @author Tony Morris, Workingmouse * @version 1.0, 11/10/2008 */ -final case class Left[+A, +B](a: A) extends Either[A, B] { override def isLeft = true } +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 Left side. @@ -79,41 +98,20 @@ final case class Left[+A, +B](a: A) extends Either[A, B] { override def isLeft = * @author Tony Morris, Workingmouse * @version 1.0, 11/10/2008 */ -final case class Right[+A, +B](b: B) extends Either[A, B] { override def isRight = true } +final case class Right[+A, +B](b: B) extends Either[A, B] { + def isLeft = false + def isRight = true +} object Either { - - /** - * Returns the Left values in the given Iterable of Eithers. - */ - @deprecated("use `for (Left(a) <- es) yield a'") - 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 Right values in the givenIterable of Eithers. - */ - @deprecated("use `for (Right(a) <- es) yield a'") - 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. - */ - @deprecated("use `for ((Left(l), Right(r)) <- es partition isLeft) yield (l, r)'") - 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) + class MergeableEither[A](x: Either[A, A]) { + def merge: A = x match { + case Left(a) => a + case Right(a) => a } + } + + implicit def either2mergeable[A](x: Either[A, A]): MergeableEither[A] = new MergeableEither(x) /** * Projects an Either into a Left. @@ -315,15 +313,11 @@ object Either { } } - /** - * Joins an Either through Left. - */ + @deprecated("use `x.joinLeft'") def joinLeft[A, B](es: Either[Either[A, B], B]) = es.left.flatMap(x => x) - /** - * Joins an Either through Right. - */ + @deprecated("use `x.joinRight'") def joinRight[A, B](es: Either[A, Either[A, B]]) = es.right.flatMap(x => x) @@ -331,14 +325,47 @@ object Either { * Takes an Either to its contained value within Left or * Right. */ + @deprecated("use `x.merge'") def merge[T](e: Either[T, T]) = e match { case Left(t) => t case Right(t) => t } + /** + * Returns the Left values in the given Iterable of Eithers. + */ + @deprecated("use `for (Left(a) <- es) yield a'") + 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 Right values in the givenIterable of Eithers. + */ + @deprecated("use `for (Right(a) <- es) yield a'") + 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. + */ + @deprecated("use `for ((Left(l), Right(r)) <- es partition isLeft) yield (l, r)'") + 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) + } + /** If the condition satisfies, return the given A in Left, * otherwise, return the given B in Right. */ - def cond[A, B](test: Boolean, right: => B, left: => A) = - if(test) Right(right) else Left(left) + def cond[A, B](test: Boolean, right: => B, left: => A): Either[A, B] = + if (test) Right(right) else Left(left) } diff --git a/src/library/scala/Option.scala b/src/library/scala/Option.scala index a834623a37..510fb6c05b 100644 --- a/src/library/scala/Option.scala +++ b/src/library/scala/Option.scala @@ -30,19 +30,6 @@ object Option */ @experimental def apply[A](x: A): Option[A] = if (x == null) None else Some(x) - - // For methods which return -1 on failure - // def fromReturnValue(value: Int): Option[Int] = if (value < 0) None else Some(value) - - class NullableOption[A >: Null <: AnyRef](x: Option[A]) { - /** The option's value if it is nonempty, or null if it is empty. - * The use of null of course is discouraged, but code written to use Options - * often must interface with code which expects and returns nulls. - */ - @experimental - def orNull: A = if (x.isEmpty) null else x.get - } - implicit def option2NullableOption[A >: Null <: AnyRef](xo: Option[A]): NullableOption[A] = new NullableOption(xo) } /** This class represents optional values. Instances of Option @@ -89,6 +76,13 @@ sealed abstract class Option[+A] extends Product { def orZero[B >: A](implicit z: Zero[B]): B = this getOrElse z.zero + /** The option's value if it is nonempty, or null if it is empty. + * The use of null of course is discouraged, but code written to use Options + * often must interface with code which expects and returns nulls. + */ + @experimental + def orNull[A1 >: A](implicit ev: Null <:< A1): A1 = this getOrElse null + /** If the option is nonempty, return a function applied to its value, * wrapped in a Some i.e. Some(f(this.get)). * Otherwise return None. -- cgit v1.2.3