Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace Xor with Either, fixes #1192 #1289

Merged
merged 8 commits into from
Aug 20, 2016
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
import scala.util.{ Failure, Success, Try }
import scala.util.control.NonFatal

Expand Down Expand Up @@ -37,21 +37,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
17 changes: 8 additions & 9 deletions core/src/main/scala/cats/Bitraverse.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,23 @@ import simulacrum.typeclass
*
* Example:
* {{{
* scala> import cats.data.Xor
* scala> import cats.implicits._
*
* scala> val rightSome: Option[String] Xor Option[Int] = Xor.right(Some(3))
* scala> val rightSome: Either[Option[String], Option[Int]] = Either.right(Some(3))
* scala> rightSome.bisequence
* res0: Option[String Xor Int] = Some(Right(3))
* res0: Option[Either[String, Int]] = Some(Right(3))
*
* scala> val rightNone: Option[String] Xor Option[Int] = Xor.right(None)
* scala> val rightNone: Either[Option[String], Option[Int]] = Either.right(None)
* scala> rightNone.bisequence
* res1: Option[String Xor Int] = None
* res1: Option[Either[String, Int]] = None
*
* scala> val leftSome: Option[String] Xor Option[Int] = Xor.left(Some("foo"))
* scala> val leftSome: Either[Option[String], Option[Int]] = Either.left(Some("foo"))
* scala> leftSome.bisequence
* res2: Option[String Xor Int] = Some(Left(foo))
* res2: Option[Either[String, Int]] = Some(Left(foo))
*
* scala> val leftNone: Option[String] Xor Option[Int] = Xor.left(None)
* scala> val leftNone: Either[Option[String], Option[Int]] = Either.left(None)
* scala> leftNone.bisequence
* res3: Option[String Xor Int] = None
* res3: Option[Either[String, Int]] = None
* }}}
*/
def bisequence[G[_]: Applicative, A, B](fab: F[G[A], G[B]]): G[F[A, B]] =
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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we change this to defaultTailRecM(a)(f)?

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
5 changes: 2 additions & 3 deletions core/src/main/scala/cats/FlatMap.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package cats

import cats.data.Xor
import simulacrum.typeclass

/**
Expand Down Expand Up @@ -93,7 +92,7 @@ import simulacrum.typeclass
flatMap(fa)(if (_) ifTrue else ifFalse)

/**
* Keeps calling `f` until a `[[cats.data.Xor.Right Right]][B]` is returned.
* Keeps calling `f` until a `[[Right]][B]` is returned.
*
* Based on Phil Freeman's
* [[http://functorial.com/stack-safety-for-free/index.pdf Stack Safety for Free]].
Expand All @@ -106,5 +105,5 @@ import simulacrum.typeclass
* using recursive flatMap. Such an implementation will only be stack safe if
* the Monad is trampolined.
*/
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
7 changes: 3 additions & 4 deletions core/src/main/scala/cats/Monad.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package cats

import cats.data.Xor
import simulacrum.typeclass

/**
Expand All @@ -22,9 +21,9 @@ import simulacrum.typeclass
* to write this method (all cats types have a stack safe version
* of this). When this method is safe you can find an `implicit r: RecursiveTailRecM`.
*/
protected def defaultTailRecM[A, B](a: A)(fn: A => F[A Xor B]): F[B] =
protected def defaultTailRecM[A, B](a: A)(fn: A => F[Either[A, B]]): F[B] =
flatMap(fn(a)) {
case Xor.Right(b) => pure(b)
case Xor.Left(nextA) => defaultTailRecM(nextA)(fn)
case Right(b) => pure(b)
case Left(nextA) => defaultTailRecM(nextA)(fn)
}
}
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
21 changes: 8 additions & 13 deletions core/src/main/scala/cats/arrow/Choice.scala
Original file line number Diff line number Diff line change
@@ -1,50 +1,45 @@
package cats
package arrow

import cats.data.Xor

import simulacrum.typeclass

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

/**
* Given two `F`s (`f` and `g`) with a common target type, create a new `F`
* with the same target type, but with a source type of either `f`'s source
* type OR `g`'s source type.
*
* 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)
}
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
8 changes: 4 additions & 4 deletions core/src/main/scala/cats/data/Cokleisli.scala
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,12 @@ private[data] sealed abstract class CokleisliInstances extends CokleisliInstance
override def map[B, C](fa: Cokleisli[F, A, B])(f: B => C): Cokleisli[F, A, C] =
fa.map(f)

def tailRecM[B, C](b: B)(fn: B => Cokleisli[F, A, B Xor C]): Cokleisli[F, A, C] =
def tailRecM[B, C](b: B)(fn: B => Cokleisli[F, A, Either[B, C]]): Cokleisli[F, A, C] =
Cokleisli({ (fa: F[A]) =>
@tailrec
def loop(c: Cokleisli[F, A, B Xor C]): C = c.run(fa) match {
case Xor.Right(c) => c
case Xor.Left(bb) => loop(fn(bb))
def loop(c: Cokleisli[F, A, Either[B, C]]): C = c.run(fa) match {
case Right(c) => c
case Left(bb) => loop(fn(bb))
}
loop(fn(b))
})
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