Skip to content

Commit

Permalink
Prefer Either over Xor, fixes #1192
Browse files Browse the repository at this point in the history
This commit keeps Xor and XorT but moves most Cats functions from Xor to Either.
Xor will be removed after 0.8.0.

- Add methods from Xor onto enrichment class of Either
- Copy XorT into an EitherT based around Either
- Styling: Occurences of Either are styled as Either[A, B] instead of A Either B (as it was in Xor)
  • Loading branch information
adelbertc committed Aug 15, 2016
1 parent 60ca2b7 commit 3bc775f
Show file tree
Hide file tree
Showing 51 changed files with 911 additions and 252 deletions.
16 changes: 8 additions & 8 deletions core/src/main/scala/cats/ApplicativeError.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package cats

import cats.data.{Xor, XorT}
import cats.data.EitherT

/**
* An applicative that also allows you to raise and or handle an error value.
Expand Down Expand Up @@ -35,21 +35,21 @@ trait ApplicativeError[F[_], E] extends Applicative[F] {
def handleError[A](fa: F[A])(f: E => A): F[A] = handleErrorWith(fa)(f andThen pure)

/**
* Handle errors by turning them into [[cats.data.Xor.Left]] values.
* Handle errors by turning them into [[scala.util.Either]] values.
*
* If there is no error, then an [[cats.data.Xor.Right]] value will be returned instead.
* If there is no error, then an [[scala.util.Right]] value will be returned instead.
*
* All non-fatal errors should be handled by this method.
*/
def attempt[A](fa: F[A]): F[E Xor A] = handleErrorWith(
map(fa)(Xor.right[E, A])
)(e => pure(Xor.left(e)))
def attempt[A](fa: F[A]): F[Either[E, A]] = handleErrorWith(
map(fa)(Right(_): Either[E, A])
)(e => pure(Left(e)))

/**
* Similar to [[attempt]], but wraps the result in a [[cats.data.XorT]] for
* Similar to [[attempt]], but wraps the result in a [[cats.data.EitherT]] for
* convenience.
*/
def attemptT[A](fa: F[A]): XorT[F, E, A] = XorT(attempt(fa))
def attemptT[A](fa: F[A]): EitherT[F, E, A] = EitherT(attempt(fa))

/**
* Recover from certain errors by mapping them to an `A` value.
Expand Down
7 changes: 3 additions & 4 deletions core/src/main/scala/cats/Eval.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package cats

import scala.annotation.tailrec
import cats.data.Xor
import cats.syntax.all._

/**
Expand Down Expand Up @@ -302,10 +301,10 @@ private[cats] trait EvalInstances extends EvalInstances0 {
def flatMap[A, B](fa: Eval[A])(f: A => Eval[B]): Eval[B] = fa.flatMap(f)
def extract[A](la: Eval[A]): A = la.value
def coflatMap[A, B](fa: Eval[A])(f: Eval[A] => B): Eval[B] = Later(f(fa))
def tailRecM[A, B](a: A)(f: A => Eval[A Xor B]): Eval[B] =
def tailRecM[A, B](a: A)(f: A => Eval[Either[A, B]]): Eval[B] =
f(a).flatMap(_ match {
case Xor.Left(a1) => tailRecM(a1)(f) // recursion OK here, since flatMap is lazy
case Xor.Right(b) => Eval.now(b)
case Left(a1) => tailRecM(a1)(f) // recursion OK here, since flatMap is lazy
case Right(b) => Eval.now(b)
})
}

Expand Down
6 changes: 2 additions & 4 deletions core/src/main/scala/cats/FlatMapRec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package cats

import simulacrum.typeclass

import cats.data.Xor

/**
* Version of [[cats.FlatMap]] capable of stack-safe recursive `flatMap`s.
*
Expand All @@ -13,12 +11,12 @@ import cats.data.Xor
@typeclass trait FlatMapRec[F[_]] extends FlatMap[F] {

/**
* Keeps calling `f` until a `[[cats.data.Xor.Right Right]][B]` is returned.
* Keeps calling `f` until a `[[scala.util.Right]][B]` is returned.
*
* Implementations of this method must use constant stack space.
*
* `f` must use constant stack space. (It is OK to use a constant number of
* `map`s and `flatMap`s inside `f`.)
*/
def tailRecM[A, B](a: A)(f: A => F[A Xor B]): F[B]
def tailRecM[A, B](a: A)(f: A => F[Either[A, B]]): F[B]
}
27 changes: 12 additions & 15 deletions core/src/main/scala/cats/Foldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,8 @@ import simulacrum.typeclass
* For example:
*
* {{{
* scala> import cats.data.Xor
* scala> import cats.implicits._
* scala> def parseInt(s: String): Option[Int] = Xor.catchOnly[NumberFormatException](s.toInt).toOption
* scala> def parseInt(s: String): Option[Int] = Either.catchOnly[NumberFormatException](s.toInt).toOption
* scala> val F = Foldable[List]
* scala> F.traverse_(List("333", "444"))(parseInt)
* res0: Option[Unit] = Some(())
Expand All @@ -208,19 +207,18 @@ import simulacrum.typeclass
/**
* Behaves like traverse_, but uses [[Unapply]] to find the
* [[Applicative]] instance for `G` - used when `G` is a
* type constructor with two or more parameters such as [[cats.data.Xor]]
* type constructor with two or more parameters such as [[scala.util.Either]]
*
* {{{
* scala> import cats.data.Xor
* scala> import cats.implicits._
* scala> def parseInt(s: String): Xor[String, Int] =
* | try { Xor.Right(s.toInt) }
* | catch { case _: NumberFormatException => Xor.Left("boo") }
* scala> def parseInt(s: String): Either[String, Int] =
* | try { Right(s.toInt) }
* | catch { case _: NumberFormatException => Left("boo") }
* scala> val F = Foldable[List]
* scala> F.traverseU_(List("333", "444"))(parseInt)
* res0: Xor[String, Unit] = Right(())
* res0: Either[String, Unit] = Right(())
* scala> F.traverseU_(List("333", "zzz"))(parseInt)
* res1: Xor[String, Unit] = Left(boo)
* res1: Either[String, Unit] = Left(boo)
* }}}
*
* Note that using `traverse_` instead of `traverseU_` would not compile without
Expand Down Expand Up @@ -253,16 +251,15 @@ import simulacrum.typeclass
/**
* Behaves like sequence_, but uses [[Unapply]] to find the
* [[Applicative]] instance for `G` - used when `G` is a
* type constructor with two or more parameters such as [[cats.data.Xor]]
* type constructor with two or more parameters such as [[scala.util.Either]]
*
* {{{
* scala> import cats.data.Xor
* scala> import cats.implicits._
* scala> val F = Foldable[List]
* scala> F.sequenceU_(List(Xor.right[String, Int](333), Xor.Right(444)))
* res0: Xor[String, Unit] = Right(())
* scala> F.sequenceU_(List(Xor.right[String, Int](333), Xor.Left("boo")))
* res1: Xor[String, Unit] = Left(boo)
* scala> F.sequenceU_(List(Either.right[String, Int](333), Right(444)))
* res0: Either[String, Unit] = Right(())
* scala> F.sequenceU_(List(Either.right[String, Int](333), Left("boo")))
* res1: Either[String, Unit] = Left(boo)
* }}}
*
* Note that using `sequence_` instead of `sequenceU_` would not compile without
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/MonadCombine.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import simulacrum.typeclass
* Fold over the inner structure to combine all of the values with
* our combine method inherited from MonoidK. The result is for us
* to accumulate all of the "interesting" values of the inner G, so
* if G is Option, we collect all the Some values, if G is Xor,
* if G is Option, we collect all the Some values, if G is Either,
* we collect all the Right values, etc.
*/
def unite[G[_], A](fga: F[G[A]])(implicit G: Foldable[G]): F[A] =
Expand Down
15 changes: 6 additions & 9 deletions core/src/main/scala/cats/Traverse.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@ import simulacrum.typeclass
*
* Example:
* {{{
* scala> import cats.data.Xor
* scala> import cats.implicits._
* scala> def parseInt(s: String): Option[Int] = Xor.catchOnly[NumberFormatException](s.toInt).toOption
* scala> def parseInt(s: String): Option[Int] = Either.catchOnly[NumberFormatException](s.toInt).toOption
* scala> List("1", "2", "3").traverse(parseInt)
* res0: Option[List[Int]] = Some(List(1, 2, 3))
* scala> List("1", "two", "3").traverse(parseInt)
Expand All @@ -39,14 +38,13 @@ import simulacrum.typeclass
*
* Example:
* {{{
* scala> import cats.data.Xor
* scala> import cats.implicits._
* scala> def parseInt(s: String): Xor[String, Int] = Xor.catchOnly[NumberFormatException](s.toInt).leftMap(_ => "no number")
* scala> def parseInt(s: String): Either[String, Int] = Either.catchOnly[NumberFormatException](s.toInt).leftMap(_ => "no number")
* scala> val ns = List("1", "2", "3")
* scala> ns.traverseU(parseInt)
* res0: Xor[String, List[Int]] = Right(List(1, 2, 3))
* scala> ns.traverse[Xor[String, ?], Int](parseInt)
* res1: Xor[String, List[Int]] = Right(List(1, 2, 3))
* res0: Either[String, List[Int]] = Right(List(1, 2, 3))
* scala> ns.traverse[Either[String, ?], Int](parseInt)
* res1: Either[String, List[Int]] = Right(List(1, 2, 3))
* }}}
*/
def traverseU[A, GB](fa: F[A])(f: A => GB)(implicit U: Unapply[Applicative, GB]): U.M[F[U.A]] =
Expand All @@ -57,9 +55,8 @@ import simulacrum.typeclass
*
* Example:
* {{{
* scala> import cats.data.Xor
* scala> import cats.implicits._
* scala> def parseInt(s: String): Option[Int] = Xor.catchOnly[NumberFormatException](s.toInt).toOption
* scala> def parseInt(s: String): Option[Int] = Either.catchOnly[NumberFormatException](s.toInt).toOption
* scala> val x = Option(List("1", "two", "3"))
* scala> x.traverseM(_.map(parseInt))
* res0: List[Option[Int]] = List(Some(1), None, Some(3))
Expand Down
20 changes: 8 additions & 12 deletions core/src/main/scala/cats/arrow/Choice.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package cats
package arrow

import cats.data.Xor

trait Choice[F[_, _]] extends Category[F] {

/**
Expand All @@ -12,39 +10,37 @@ trait Choice[F[_, _]] extends Category[F] {
*
* Example:
* {{{
* scala> import cats.data.Xor
* scala> import cats.implicits._
* scala> val b: Boolean => String = _ + " is a boolean"
* scala> val i: Int => String = _ + " is an integer"
* scala> val f: (Boolean Xor Int) => String = Choice[Function1].choice(b, i)
* scala> val f: (Either[Boolean, Int]) => String = Choice[Function1].choice(b, i)
*
* scala> f(Xor.right(3))
* scala> f(Right(3))
* res0: String = 3 is an integer
*
* scala> f(Xor.left(false))
* scala> f(Left(false))
* res0: String = false is a boolean
* }}}
*/
def choice[A, B, C](f: F[A, C], g: F[B, C]): F[Xor[A, B], C]
def choice[A, B, C](f: F[A, C], g: F[B, C]): F[Either[A, B], C]

/**
* An `F` that, given a source `A` on either the right or left side, will
* return that same `A` object.
*
* Example:
* {{{
* scala> import cats.data.Xor
* scala> import cats.implicits._
* scala> val f: (Int Xor Int) => Int = Choice[Function1].codiagonal[Int]
* scala> val f: (Either[Int, Int]) => Int = Choice[Function1].codiagonal[Int]
*
* scala> f(Xor.right(3))
* scala> f(Right(3))
* res0: Int = 3
*
* scala> f(Xor.left(3))
* scala> f(Left(3))
* res1: Int = 3
* }}}
*/
def codiagonal[A]: F[Xor[A, A], A] = choice(id, id)
def codiagonal[A]: F[Either[A, A], A] = choice(id, id)
}

object Choice {
Expand Down
6 changes: 3 additions & 3 deletions core/src/main/scala/cats/arrow/FunctionK.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package cats
package arrow

import cats.data.{Xor, Coproduct}
import cats.data. Coproduct

trait FunctionK[F[_], G[_]] extends Serializable { self =>
def apply[A](fa: F[A]): G[A]
Expand All @@ -17,8 +17,8 @@ trait FunctionK[F[_], G[_]] extends Serializable { self =>
def or[H[_]](h: FunctionK[H, G]): FunctionK[Coproduct[F, H, ?], G] =
new FunctionK[Coproduct[F, H, ?], G] {
def apply[A](fa: Coproduct[F, H, A]): G[A] = fa.run match {
case Xor.Left(ff) => self(ff)
case Xor.Right(gg) => h(gg)
case Left(ff) => self(ff)
case Right(gg) => h(gg)
}
}
}
Expand Down
17 changes: 9 additions & 8 deletions core/src/main/scala/cats/data/Coproduct.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ package data

import cats.arrow.FunctionK
import cats.functor.Contravariant
import cats.syntax.either._

/** `F` on the left and `G` on the right of [[Xor]].
/** `F` on the left and `G` on the right of [[scala.util.Either]].
*
* @param run The underlying [[Xor]].
* @param run The underlying [[scala.util.Either]].
*/
final case class Coproduct[F[_], G[_], A](run: F[A] Xor G[A]) {
final case class Coproduct[F[_], G[_], A](run: Either[F[A], G[A]]) {

import Coproduct._

Expand Down Expand Up @@ -86,17 +87,17 @@ final case class Coproduct[F[_], G[_], A](run: F[A] Xor G[A]) {
object Coproduct extends CoproductInstances {

def leftc[F[_], G[_], A](x: F[A]): Coproduct[F, G, A] =
Coproduct(Xor.left(x))
Coproduct(Left(x))

def rightc[F[_], G[_], A](x: G[A]): Coproduct[F, G, A] =
Coproduct(Xor.right(x))
Coproduct(Right(x))

final class CoproductLeft[G[_]] private[Coproduct] {
def apply[F[_], A](fa: F[A]): Coproduct[F, G, A] = Coproduct(Xor.left(fa))
def apply[F[_], A](fa: F[A]): Coproduct[F, G, A] = Coproduct(Left(fa))
}

final class CoproductRight[F[_]] private[Coproduct] {
def apply[G[_], A](ga: G[A]): Coproduct[F, G, A] = Coproduct(Xor.right(ga))
def apply[G[_], A](ga: G[A]): Coproduct[F, G, A] = Coproduct(Right(ga))
}

def left[G[_]]: CoproductLeft[G] = new CoproductLeft[G]
Expand All @@ -106,7 +107,7 @@ object Coproduct extends CoproductInstances {

private[data] sealed abstract class CoproductInstances3 {

implicit def catsDataEqForCoproduct[F[_], G[_], A](implicit E: Eq[F[A] Xor G[A]]): Eq[Coproduct[F, G, A]] =
implicit def catsDataEqForCoproduct[F[_], G[_], A](implicit E: Eq[Either[F[A], G[A]]]): Eq[Coproduct[F, G, A]] =
Eq.by(_.run)

implicit def catsDataFunctorForCoproduct[F[_], G[_]](implicit F0: Functor[F], G0: Functor[G]): Functor[Coproduct[F, G, ?]] =
Expand Down
Loading

0 comments on commit 3bc775f

Please sign in to comment.