diff --git a/core/src/main/scala/cats/ApplicativeError.scala b/core/src/main/scala/cats/ApplicativeError.scala index 4f36625205..91f726a6db 100644 --- a/core/src/main/scala/cats/ApplicativeError.scala +++ b/core/src/main/scala/cats/ApplicativeError.scala @@ -1,6 +1,6 @@ package cats -import cats.data.EitherT +import cats.data.{Xor, XorT} import scala.util.{ Failure, Success, Try } import scala.util.control.NonFatal @@ -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 [[scala.util.Either]] values. + * Handle errors by turning them into [[cats.data.Xor]] values. * - * If there is no error, then an `scala.util.Right` value will be returned instead. + * If there is no error, then an [[cats.data.Xor.Right]] value will be returned instead. * * All non-fatal errors should be handled by this method. */ - def attempt[A](fa: F[A]): F[Either[E, A]] = handleErrorWith( - map(fa)(Right(_): Either[E, A]) - )(e => pure(Left(e))) + def attempt[A](fa: F[A]): F[Xor[E, A]] = handleErrorWith( + map(fa)(Xor.Right(_): Xor[E, A]) + )(e => pure(Xor.Left(e))) /** - * Similar to [[attempt]], but wraps the result in a [[cats.data.EitherT]] for + * Similar to [[attempt]], but wraps the result in a [[cats.data.XorT]] for * convenience. */ - def attemptT[A](fa: F[A]): EitherT[F, E, A] = EitherT(attempt(fa)) + def attemptT[A](fa: F[A]): XorT[F, E, A] = XorT(attempt(fa)) /** * Recover from certain errors by mapping them to an `A` value. diff --git a/core/src/main/scala/cats/Bitraverse.scala b/core/src/main/scala/cats/Bitraverse.scala index 27767141f4..fdb7708082 100644 --- a/core/src/main/scala/cats/Bitraverse.scala +++ b/core/src/main/scala/cats/Bitraverse.scala @@ -17,23 +17,24 @@ import simulacrum.typeclass * * Example: * {{{ + * scala> import cats.data.Xor * scala> import cats.implicits._ * - * scala> val rightSome: Either[Option[String], Option[Int]] = Either.right(Some(3)) + * scala> val rightSome: Option[String] Xor Option[Int] = Xor.right(Some(3)) * scala> rightSome.bisequence - * res0: Option[Either[String, Int]] = Some(Right(3)) + * res0: Option[String Xor Int] = Some(Right(3)) * - * scala> val rightNone: Either[Option[String], Option[Int]] = Either.right(None) + * scala> val rightNone: Option[String] Xor Option[Int] = Xor.right(None) * scala> rightNone.bisequence - * res1: Option[Either[String, Int]] = None + * res1: Option[String Xor Int] = None * - * scala> val leftSome: Either[Option[String], Option[Int]] = Either.left(Some("foo")) + * scala> val leftSome: Option[String] Xor Option[Int] = Xor.left(Some("foo")) * scala> leftSome.bisequence - * res2: Option[Either[String, Int]] = Some(Left(foo)) + * res2: Option[String Xor Int] = Some(Left(foo)) * - * scala> val leftNone: Either[Option[String], Option[Int]] = Either.left(None) + * scala> val leftNone: Option[String] Xor Option[Int] = Xor.left(None) * scala> leftNone.bisequence - * res3: Option[Either[String, Int]] = None + * res3: Option[String Xor Int] = None * }}} */ def bisequence[G[_]: Applicative, A, B](fab: F[G[A], G[B]]): G[F[A, B]] = diff --git a/core/src/main/scala/cats/Eval.scala b/core/src/main/scala/cats/Eval.scala index 36348160fe..742b6d2592 100644 --- a/core/src/main/scala/cats/Eval.scala +++ b/core/src/main/scala/cats/Eval.scala @@ -1,6 +1,7 @@ package cats import scala.annotation.tailrec +import cats.data.Xor import cats.syntax.all._ /** @@ -301,7 +302,11 @@ 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[Either[A, B]]): Eval[B] = defaultTailRecM(a)(f) + def tailRecM[A, B](a: A)(f: A => Eval[Xor[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) + }) } implicit def catsOrderForEval[A: Order]: Order[Eval[A]] = diff --git a/core/src/main/scala/cats/FlatMap.scala b/core/src/main/scala/cats/FlatMap.scala index a7767ee808..032660f9f6 100644 --- a/core/src/main/scala/cats/FlatMap.scala +++ b/core/src/main/scala/cats/FlatMap.scala @@ -2,6 +2,8 @@ package cats import simulacrum.typeclass +import cats.data.Xor + /** * FlatMap type class gives us flatMap, which allows us to have a value * in a context (F[A]) and then feed that into a function that takes @@ -92,7 +94,7 @@ import simulacrum.typeclass flatMap(fa)(if (_) ifTrue else ifFalse) /** - * Keeps calling `f` until a `scala.util.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]]. @@ -105,5 +107,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[Either[A, B]]): F[B] + def tailRecM[A, B](a: A)(f: A => F[Xor[A, B]]): F[B] } diff --git a/core/src/main/scala/cats/Foldable.scala b/core/src/main/scala/cats/Foldable.scala index acb89624dd..00c63a38da 100644 --- a/core/src/main/scala/cats/Foldable.scala +++ b/core/src/main/scala/cats/Foldable.scala @@ -186,8 +186,9 @@ import simulacrum.typeclass * For example: * * {{{ + * scala> import cats.data.Xor * scala> import cats.implicits._ - * scala> def parseInt(s: String): Option[Int] = Either.catchOnly[NumberFormatException](s.toInt).toOption + * scala> def parseInt(s: String): Option[Int] = Xor.catchOnly[NumberFormatException](s.toInt).toOption * scala> val F = Foldable[List] * scala> F.traverse_(List("333", "444"))(parseInt) * res0: Option[Unit] = Some(()) @@ -207,18 +208,19 @@ 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 [[scala.util.Either]] + * type constructor with two or more parameters such as [[cats.data.Xor]] * * {{{ + * scala> import cats.data.Xor * scala> import cats.implicits._ - * scala> def parseInt(s: String): Either[String, Int] = - * | try { Right(s.toInt) } - * | catch { case _: NumberFormatException => Left("boo") } + * scala> def parseInt(s: String): Xor[String, Int] = + * | try { Xor.Right(s.toInt) } + * | catch { case _: NumberFormatException => Xor.Left("boo") } * scala> val F = Foldable[List] * scala> F.traverseU_(List("333", "444"))(parseInt) - * res0: Either[String, Unit] = Right(()) + * res0: Xor[String, Unit] = Right(()) * scala> F.traverseU_(List("333", "zzz"))(parseInt) - * res1: Either[String, Unit] = Left(boo) + * res1: Xor[String, Unit] = Left(boo) * }}} * * Note that using `traverse_` instead of `traverseU_` would not compile without @@ -251,15 +253,16 @@ 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 [[scala.util.Either]] + * type constructor with two or more parameters such as [[cats.data.Xor]] * * {{{ + * scala> import cats.data.Xor * scala> import cats.implicits._ * scala> val F = Foldable[List] - * 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) + * 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) * }}} * * Note that using `sequence_` instead of `sequenceU_` would not compile without diff --git a/core/src/main/scala/cats/Monad.scala b/core/src/main/scala/cats/Monad.scala index b8388fa7b5..e7deac5ec8 100644 --- a/core/src/main/scala/cats/Monad.scala +++ b/core/src/main/scala/cats/Monad.scala @@ -2,6 +2,8 @@ package cats import simulacrum.typeclass +import cats.data.Xor + /** * Monad. * @@ -21,9 +23,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[Either[A, B]]): F[B] = + protected def defaultTailRecM[A, B](a: A)(fn: A => F[Xor[A, B]]): F[B] = flatMap(fn(a)) { - case Right(b) => pure(b) - case Left(nextA) => defaultTailRecM(nextA)(fn) + case Xor.Right(b) => pure(b) + case Xor.Left(nextA) => defaultTailRecM(nextA)(fn) } } diff --git a/core/src/main/scala/cats/MonadCombine.scala b/core/src/main/scala/cats/MonadCombine.scala index a418cba774..55cb76184e 100644 --- a/core/src/main/scala/cats/MonadCombine.scala +++ b/core/src/main/scala/cats/MonadCombine.scala @@ -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 Either, + * if G is Option, we collect all the Some values, if G is Xor, * we collect all the Right values, etc. */ def unite[G[_], A](fga: F[G[A]])(implicit G: Foldable[G]): F[A] = diff --git a/core/src/main/scala/cats/Traverse.scala b/core/src/main/scala/cats/Traverse.scala index 7e34eef82b..9aef6df102 100644 --- a/core/src/main/scala/cats/Traverse.scala +++ b/core/src/main/scala/cats/Traverse.scala @@ -22,8 +22,9 @@ import simulacrum.typeclass * * Example: * {{{ + * scala> import cats.data.Xor * scala> import cats.implicits._ - * scala> def parseInt(s: String): Option[Int] = Either.catchOnly[NumberFormatException](s.toInt).toOption + * scala> def parseInt(s: String): Option[Int] = Xor.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) @@ -38,13 +39,14 @@ import simulacrum.typeclass * * Example: * {{{ + * scala> import cats.data.Xor * scala> import cats.implicits._ - * scala> def parseInt(s: String): Either[String, Int] = Either.catchOnly[NumberFormatException](s.toInt).leftMap(_ => "no number") + * scala> def parseInt(s: String): Xor[String, Int] = Xor.catchOnly[NumberFormatException](s.toInt).leftMap(_ => "no number") * scala> val ns = List("1", "2", "3") * scala> ns.traverseU(parseInt) - * 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)) + * 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)) * }}} */ def traverseU[A, GB](fa: F[A])(f: A => GB)(implicit U: Unapply[Applicative, GB]): U.M[F[U.A]] = @@ -55,8 +57,9 @@ import simulacrum.typeclass * * Example: * {{{ + * scala> import cats.data.Xor * scala> import cats.implicits._ - * scala> def parseInt(s: String): Option[Int] = Either.catchOnly[NumberFormatException](s.toInt).toOption + * scala> def parseInt(s: String): Option[Int] = Xor.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)) diff --git a/core/src/main/scala/cats/arrow/Choice.scala b/core/src/main/scala/cats/arrow/Choice.scala index 7c1b4ba008..a72b2c53fd 100644 --- a/core/src/main/scala/cats/arrow/Choice.scala +++ b/core/src/main/scala/cats/arrow/Choice.scala @@ -1,6 +1,8 @@ package cats package arrow +import cats.data.Xor + import simulacrum.typeclass @typeclass trait Choice[F[_, _]] extends Category[F] { @@ -11,19 +13,20 @@ import simulacrum.typeclass * * 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: (Either[Boolean, Int]) => String = Choice[Function1].choice(b, i) + * scala> val f: (Boolean Xor Int) => String = Choice[Function1].choice(b, i) * - * scala> f(Right(3)) + * scala> f(Xor.right(3)) * res0: String = 3 is an integer * - * scala> f(Left(false)) + * scala> f(Xor.left(false)) * res0: String = false is a boolean * }}} */ - def choice[A, B, C](f: F[A, C], g: F[B, C]): F[Either[A, B], C] + def choice[A, B, C](f: F[A, C], g: F[B, C]): F[Xor[A, B], C] /** * An `F` that, given a source `A` on either the right or left side, will @@ -31,15 +34,16 @@ import simulacrum.typeclass * * Example: * {{{ + * scala> import cats.data.Xor * scala> import cats.implicits._ - * scala> val f: (Either[Int, Int]) => Int = Choice[Function1].codiagonal[Int] + * scala> val f: (Int Xor Int) => Int = Choice[Function1].codiagonal[Int] * - * scala> f(Right(3)) + * scala> f(Xor.right(3)) * res0: Int = 3 * - * scala> f(Left(3)) + * scala> f(Xor.left(3)) * res1: Int = 3 * }}} */ - def codiagonal[A]: F[Either[A, A], A] = choice(id, id) + def codiagonal[A]: F[Xor[A, A], A] = choice(id, id) } diff --git a/core/src/main/scala/cats/arrow/FunctionK.scala b/core/src/main/scala/cats/arrow/FunctionK.scala index 4cf392d3ff..eaadb4d598 100644 --- a/core/src/main/scala/cats/arrow/FunctionK.scala +++ b/core/src/main/scala/cats/arrow/FunctionK.scala @@ -1,7 +1,7 @@ package cats package arrow -import cats.data. Coproduct +import cats.data.{Xor, Coproduct} trait FunctionK[F[_], G[_]] extends Serializable { self => def apply[A](fa: F[A]): G[A] @@ -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 Left(ff) => self(ff) - case Right(gg) => h(gg) + case Xor.Left(ff) => self(ff) + case Xor.Right(gg) => h(gg) } } } diff --git a/core/src/main/scala/cats/data/Cokleisli.scala b/core/src/main/scala/cats/data/Cokleisli.scala index 752a200c74..669d7244fc 100644 --- a/core/src/main/scala/cats/data/Cokleisli.scala +++ b/core/src/main/scala/cats/data/Cokleisli.scala @@ -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, Either[B, C]]): Cokleisli[F, A, C] = + def tailRecM[B, C](b: B)(fn: B => Cokleisli[F, A, Xor[B, C]]): Cokleisli[F, A, C] = Cokleisli({ (fa: F[A]) => @tailrec - def loop(c: Cokleisli[F, A, Either[B, C]]): C = c.run(fa) match { - case Right(c) => c - case Left(bb) => loop(fn(bb)) + def loop(c: Cokleisli[F, A, Xor[B, C]]): C = c.run(fa) match { + case Xor.Right(c) => c + case Xor.Left(bb) => loop(fn(bb)) } loop(fn(b)) }) diff --git a/core/src/main/scala/cats/data/Coproduct.scala b/core/src/main/scala/cats/data/Coproduct.scala index cc552b6c0d..35b4f8a638 100644 --- a/core/src/main/scala/cats/data/Coproduct.scala +++ b/core/src/main/scala/cats/data/Coproduct.scala @@ -3,13 +3,12 @@ package data import cats.arrow.FunctionK import cats.functor.Contravariant -import cats.syntax.either._ -/** `F` on the left and `G` on the right of [[scala.util.Either]]. +/** `F` on the left and `G` on the right of [[Xor]]. * - * @param run The underlying [[scala.util.Either]]. + * @param run The underlying [[Xor]]. */ -final case class Coproduct[F[_], G[_], A](run: Either[F[A], G[A]]) { +final case class Coproduct[F[_], G[_], A](run: F[A] Xor G[A]) { import Coproduct._ @@ -87,17 +86,17 @@ final case class Coproduct[F[_], G[_], A](run: Either[F[A], G[A]]) { object Coproduct extends CoproductInstances { def leftc[F[_], G[_], A](x: F[A]): Coproduct[F, G, A] = - Coproduct(Left(x)) + Coproduct(Xor.left(x)) def rightc[F[_], G[_], A](x: G[A]): Coproduct[F, G, A] = - Coproduct(Right(x)) + Coproduct(Xor.right(x)) final class CoproductLeft[G[_]] private[Coproduct] { - def apply[F[_], A](fa: F[A]): Coproduct[F, G, A] = Coproduct(Left(fa)) + def apply[F[_], A](fa: F[A]): Coproduct[F, G, A] = Coproduct(Xor.left(fa)) } final class CoproductRight[F[_]] private[Coproduct] { - def apply[G[_], A](ga: G[A]): Coproduct[F, G, A] = Coproduct(Right(ga)) + def apply[G[_], A](ga: G[A]): Coproduct[F, G, A] = Coproduct(Xor.right(ga)) } def left[G[_]]: CoproductLeft[G] = new CoproductLeft[G] @@ -107,7 +106,7 @@ object Coproduct extends CoproductInstances { private[data] sealed abstract class CoproductInstances3 { - implicit def catsDataEqForCoproduct[F[_], G[_], A](implicit E: Eq[Either[F[A], G[A]]]): Eq[Coproduct[F, G, A]] = + implicit def catsDataEqForCoproduct[F[_], G[_], A](implicit E: Eq[F[A] Xor 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, ?]] = diff --git a/core/src/main/scala/cats/data/EitherT.scala b/core/src/main/scala/cats/data/EitherT.scala deleted file mode 100644 index 31d7245dfc..0000000000 --- a/core/src/main/scala/cats/data/EitherT.scala +++ /dev/null @@ -1,467 +0,0 @@ -package cats -package data - -import cats.functor.Bifunctor -import cats.instances.either._ -import cats.syntax.either._ - -/** - * Transformer for `Either`, allowing the effect of an arbitrary type constructor `F` to be combined with the - * fail-fast effect of `Either`. - * - * `EitherT[F, A, B]` wraps a value of type `F[Either[A, B]]`. An `F[C]` can be lifted in to `EitherT[F, A, C]` via `EitherT.right`, - * and lifted in to a `EitherT[F, C, B]` via `EitherT.left`. - */ -final case class EitherT[F[_], A, B](value: F[Either[A, B]]) { - def fold[C](fa: A => C, fb: B => C)(implicit F: Functor[F]): F[C] = F.map(value)(_.fold(fa, fb)) - - def isLeft(implicit F: Functor[F]): F[Boolean] = F.map(value)(_.isLeft) - - def isRight(implicit F: Functor[F]): F[Boolean] = F.map(value)(_.isRight) - - def swap(implicit F: Functor[F]): EitherT[F, B, A] = EitherT(F.map(value)(_.swap)) - - def getOrElse(default: => B)(implicit F: Functor[F]): F[B] = F.map(value)(_.getOrElse(default)) - - def getOrElseF(default: => F[B])(implicit F: Monad[F]): F[B] = { - F.flatMap(value) { - case Left(_) => default - case Right(b) => F.pure(b) - } - } - - def orElse(default: => EitherT[F, A, B])(implicit F: Monad[F]): EitherT[F, A, B] = { - EitherT(F.flatMap(value) { - case Left(_) => default.value - case r @ Right(_) => F.pure(r) - }) - } - - def recover(pf: PartialFunction[A, B])(implicit F: Functor[F]): EitherT[F, A, B] = - EitherT(F.map(value)(_.recover(pf))) - - def recoverWith(pf: PartialFunction[A, EitherT[F, A, B]])(implicit F: Monad[F]): EitherT[F, A, B] = - EitherT(F.flatMap(value) { - case Left(a) if pf.isDefinedAt(a) => pf(a).value - case other => F.pure(other) - }) - - def valueOr(f: A => B)(implicit F: Functor[F]): F[B] = fold(f, identity) - - def forall(f: B => Boolean)(implicit F: Functor[F]): F[Boolean] = F.map(value)(_.forall(f)) - - def exists(f: B => Boolean)(implicit F: Functor[F]): F[Boolean] = F.map(value)(_.exists(f)) - - def ensure(onFailure: => A)(f: B => Boolean)(implicit F: Functor[F]): EitherT[F, A, B] = EitherT(F.map(value)(_.ensure(onFailure)(f))) - - def toOption(implicit F: Functor[F]): OptionT[F, B] = OptionT(F.map(value)(_.toOption)) - - def to[G[_]](implicit F: Functor[F], G: Alternative[G]): F[G[B]] = - F.map(value)(_.to[G]) - - def collectRight(implicit F: MonadCombine[F]): F[B] = - F.flatMap(value)(_.to[F]) - - def bimap[C, D](fa: A => C, fb: B => D)(implicit F: Functor[F]): EitherT[F, C, D] = EitherT(F.map(value)(_.bimap(fa, fb))) - - def bitraverse[G[_], C, D](f: A => G[C], g: B => G[D])(implicit traverseF: Traverse[F], applicativeG: Applicative[G]): G[EitherT[F, C, D]] = - applicativeG.map(traverseF.traverse(value)(axb => Bitraverse[Either].bitraverse(axb)(f, g)))(EitherT.apply) - - def applyAlt[D](ff: EitherT[F, A, B => D])(implicit F: Apply[F]): EitherT[F, A, D] = - EitherT[F, A, D](F.map2(this.value, ff.value)((xb, xbd) => Apply[Either[A, ?]].ap(xbd)(xb))) - - def flatMap[D](f: B => EitherT[F, A, D])(implicit F: Monad[F]): EitherT[F, A, D] = - EitherT(F.flatMap(value) { - case l @ Left(_) => F.pure(l.rightCast) - case Right(b) => f(b).value - }) - - def flatMapF[D](f: B => F[Either[A, D]])(implicit F: Monad[F]): EitherT[F, A, D] = - flatMap(f andThen EitherT.apply) - - def transform[C, D](f: Either[A, B] => Either[C, D])(implicit F: Functor[F]): EitherT[F, C, D] = - EitherT(F.map(value)(f)) - - def subflatMap[D](f: B => Either[A, D])(implicit F: Functor[F]): EitherT[F, A, D] = - transform(_.flatMap(f)) - - def map[D](f: B => D)(implicit F: Functor[F]): EitherT[F, A, D] = bimap(identity, f) - - def semiflatMap[D](f: B => F[D])(implicit F: Monad[F]): EitherT[F, A, D] = - flatMap(b => EitherT.right[F, A, D](f(b))) - - def leftMap[C](f: A => C)(implicit F: Functor[F]): EitherT[F, C, B] = bimap(f, identity) - - def compare(that: EitherT[F, A, B])(implicit o: Order[F[Either[A, B]]]): Int = - o.compare(value, that.value) - - def partialCompare(that: EitherT[F, A, B])(implicit p: PartialOrder[F[Either[A, B]]]): Double = - p.partialCompare(value, that.value) - - def ===(that: EitherT[F, A, B])(implicit eq: Eq[F[Either[A, B]]]): Boolean = - eq.eqv(value, that.value) - - def traverse[G[_], D](f: B => G[D])(implicit traverseF: Traverse[F], applicativeG: Applicative[G]): G[EitherT[F, A, D]] = - applicativeG.map(traverseF.traverse(value)(axb => Traverse[Either[A, ?]].traverse(axb)(f)))(EitherT.apply) - - def foldLeft[C](c: C)(f: (C, B) => C)(implicit F: Foldable[F]): C = - F.foldLeft(value, c)((c, axb) => axb.foldLeft(c)(f)) - - def foldRight[C](lc: Eval[C])(f: (B, Eval[C]) => Eval[C])(implicit F: Foldable[F]): Eval[C] = - F.foldRight(value, lc)((axb, lc) => axb.foldRight(lc)(f)) - - def merge(implicit ev: B <:< A, F: Functor[F]): F[A] = F.map(value)(_.fold(identity, ev.apply)) - - /** - * Similar to `Either#combine` but mapped over an `F` context. - * - * Examples: - * {{{ - * scala> import cats.data.EitherT - * scala> import cats.implicits._ - * scala> val l1: EitherT[Option, String, Int] = EitherT.left(Some("error 1")) - * scala> val l2: EitherT[Option, String, Int] = EitherT.left(Some("error 2")) - * scala> val r3: EitherT[Option, String, Int] = EitherT.right(Some(3)) - * scala> val r4: EitherT[Option, String, Int] = EitherT.right(Some(4)) - * scala> val noneEitherT: EitherT[Option, String, Int] = EitherT.left(None) - * - * scala> l1 combine l2 - * res0: EitherT[Option, String, Int] = EitherT(Some(Left(error 1))) - * - * scala> l1 combine r3 - * res1: EitherT[Option, String, Int] = EitherT(Some(Left(error 1))) - * - * scala> r3 combine l1 - * res2: EitherT[Option, String, Int] = EitherT(Some(Left(error 1))) - * - * scala> r3 combine r4 - * res3: EitherT[Option, String, Int] = EitherT(Some(Right(7))) - * - * scala> l1 combine noneEitherT - * res4: EitherT[Option, String, Int] = EitherT(None) - * - * scala> noneEitherT combine l1 - * res5: EitherT[Option, String, Int] = EitherT(None) - * - * scala> r3 combine noneEitherT - * res6: EitherT[Option, String, Int] = EitherT(None) - * - * scala> noneEitherT combine r4 - * res7: EitherT[Option, String, Int] = EitherT(None) - * }}} - */ - def combine(that: EitherT[F, A, B])(implicit F: Apply[F], B: Semigroup[B]): EitherT[F, A, B] = - EitherT(F.map2(this.value, that.value)(_ combine _)) - - def toValidated(implicit F: Functor[F]): F[Validated[A, B]] = - F.map(value)(_.toValidated) - - def toValidatedNel(implicit F: Functor[F]): F[ValidatedNel[A, B]] = - F.map(value)(_.toValidatedNel) - - /** Run this value as a `[[Validated]]` against the function and convert it back to an `[[EitherT]]`. - * - * The [[Applicative]] instance for `EitherT` "fails fast" - it is often useful to "momentarily" have - * it accumulate errors instead, which is what the `[[Validated]]` data type gives us. - * - * Example: - * {{{ - * scala> import cats.implicits._ - * scala> type Error = String - * scala> val v1: Validated[NonEmptyList[Error], Int] = Validated.Invalid(NonEmptyList.of("error 1")) - * scala> val v2: Validated[NonEmptyList[Error], Int] = Validated.Invalid(NonEmptyList.of("error 2")) - * scala> val eithert: EitherT[Option, Error, Int] = EitherT(Some(Either.left("error 3"))) - * scala> eithert.withValidated { v3 => (v1 |@| v2 |@| v3.leftMap(NonEmptyList.of(_))).map{ case (i, j, k) => i + j + k } } - * res0: EitherT[Option, NonEmptyList[Error], Int] = EitherT(Some(Left(NonEmptyList(error 1, error 2, error 3)))) - * }}} - */ - def withValidated[AA, BB](f: Validated[A, B] => Validated[AA, BB])(implicit F: Functor[F]): EitherT[F, AA, BB] = - EitherT(F.map(value)(either => f(either.toValidated).toEither)) - - def show(implicit show: Show[F[Either[A, B]]]): String = show.show(value) - - /** - * Transform this `EitherT[F, A, B]` into a `[[Nested]][F, Either[A, ?], B]`. - * - * An example where `toNested` can be used, is to get the `Apply.ap` function with the - * behavior from the composed `Apply` instances from `F` and `Either[A, ?]`, which is - * inconsistent with the behavior of the `ap` from `Monad` of `EitherT`. - * - * {{{ - * scala> import cats.data.{Nested, EitherT} - * scala> import cats.implicits._ - * scala> val ff: EitherT[List, String, Int => String] = - * | EitherT(List(Either.right(_.toString), Either.left("error"))) - * scala> val fa: EitherT[List, String, Int] = - * | EitherT(List(Either.right(1), Either.right(2))) - * scala> type ErrorOr[A] = Either[String, A] - * scala> type ListErrorOr[A] = Nested[List, ErrorOr, A] - * scala> ff.ap(fa) - * res0: EitherT[List,String,String] = EitherT(List(Right(1), Right(2), Left(error))) - * scala> EitherT((ff.toNested: ListErrorOr[Int => String]).ap(fa.toNested: ListErrorOr[Int]).value) - * res1: EitherT[List,String,String] = EitherT(List(Right(1), Right(2), Left(error), Left(error))) - * }}} - * - * Note that we need the `ErrorOr` type alias above because otherwise we can't use the - * syntax function `ap` on `Nested[List, Either[A, ?], B]`. This won't be needed after cats has - * decided [[https://github.com/typelevel/cats/issues/1073 how to handle the SI-2712 fix]]. - */ - def toNested: Nested[F, Either[A, ?], B] = Nested[F, Either[A, ?], B](value) -} - -object EitherT extends EitherTInstances with EitherTFunctions - -trait EitherTFunctions { - final def left[F[_], A, B](fa: F[A])(implicit F: Functor[F]): EitherT[F, A, B] = EitherT(F.map(fa)(Either.left)) - - final def right[F[_], A, B](fb: F[B])(implicit F: Functor[F]): EitherT[F, A, B] = EitherT(F.map(fb)(Either.right)) - - final def pure[F[_], A, B](b: B)(implicit F: Applicative[F]): EitherT[F, A, B] = right(F.pure(b)) - - /** - * Alias for [[right]] - * {{{ - * scala> import cats.data.EitherT - * scala> import cats.implicits._ - * scala> val o: Option[Int] = Some(3) - * scala> val n: Option[Int] = None - * scala> EitherT.liftT(o) - * res0: cats.data.EitherT[Option,Nothing,Int] = EitherT(Some(Right(3))) - * scala> EitherT.liftT(n) - * res1: cats.data.EitherT[Option,Nothing,Int] = EitherT(None) - * }}} - */ - final def liftT[F[_], A, B](fb: F[B])(implicit F: Functor[F]): EitherT[F, A, B] = right(fb) - - /** Transforms an `Either` into an `EitherT`, lifted into the specified `Applicative`. - * - * Note: The return type is a FromEitherPartiallyApplied[F], which has an apply method - * on it, allowing you to call fromEither like this: - * {{{ - * scala> import cats.implicits._ - * scala> val t: Either[String, Int] = Either.right(3) - * scala> EitherT.fromEither[Option](t) - * res0: EitherT[Option, String, Int] = EitherT(Some(Right(3))) - * }}} - * - * The reason for the indirection is to emulate currying type parameters. - */ - final def fromEither[F[_]]: FromEitherPartiallyApplied[F] = new FromEitherPartiallyApplied - - final class FromEitherPartiallyApplied[F[_]] private[EitherTFunctions] { - def apply[E, A](either: Either[E, A])(implicit F: Applicative[F]): EitherT[F, E, A] = - EitherT(F.pure(either)) - } -} - -private[data] abstract class EitherTInstances extends EitherTInstances1 { - - /* TODO violates right absorbtion, right distributivity, and left distributivity -- re-enable when MonadCombine laws are split in to weak/strong - implicit def catsDataMonadCombineForEitherT[F[_], L](implicit F: Monad[F], L: Monoid[L]): MonadCombine[EitherT[F, L, ?]] = { - implicit val F0 = F - implicit val L0 = L - new EitherTMonadCombine[F, L] { implicit val F = F0; implicit val L = L0 } - } - */ - - implicit def catsDataOrderForEitherT[F[_], L, R](implicit F: Order[F[Either[L, R]]]): Order[EitherT[F, L, R]] = - new EitherTOrder[F, L, R] { - val F0: Order[F[Either[L, R]]] = F - } - - implicit def catsDataShowForEitherT[F[_], L, R](implicit sh: Show[F[Either[L, R]]]): Show[EitherT[F, L, R]] = - functor.Contravariant[Show].contramap(sh)(_.value) - - implicit def catsDataBifunctorForEitherT[F[_]](implicit F: Functor[F]): Bifunctor[EitherT[F, ?, ?]] = - new Bifunctor[EitherT[F, ?, ?]] { - override def bimap[A, B, C, D](fab: EitherT[F, A, B])(f: A => C, g: B => D): EitherT[F, C, D] = fab.bimap(f, g) - } - - implicit def catsDataTraverseForEitherT[F[_], L](implicit F: Traverse[F]): Traverse[EitherT[F, L, ?]] = - new EitherTTraverse[F, L] { - val F0: Traverse[F] = F - } - - implicit def catsDataTransLiftForEitherT[E]: TransLift.Aux[EitherT[?[_], E, ?], Functor] = - new TransLift[EitherT[?[_], E, ?]] { - type TC[M[_]] = Functor[M] - - def liftT[M[_]: Functor, A](ma: M[A]): EitherT[M, E, A] = - EitherT.liftT(ma) - } - - implicit def catsMonoidForEitherT[F[_], L, A](implicit F: Monoid[F[Either[L, A]]]): Monoid[EitherT[F, L, A]] = - new EitherTMonoid[F, L, A] { implicit val F0 = F } - -} - -private[data] abstract class EitherTInstances1 extends EitherTInstances2 { - /* TODO violates monadFilter right empty law -- re-enable when MonadFilter laws are split in to weak/strong - implicit def catsDataMonadFilterForEitherT[F[_], L](implicit F: Monad[F], L: Monoid[L]): MonadFilter[EitherT[F, L, ?]] = { - implicit val F0 = F - implicit val L0 = L - new EitherTMonadFilter[F, L] { implicit val F = F0; implicit val L = L0 } - } - */ - - implicit def catsSemigroupForEitherT[F[_], L, A](implicit F: Semigroup[F[Either[L, A]]]): Semigroup[EitherT[F, L, A]] = - new EitherTSemigroup[F, L, A] { implicit val F0 = F } - - implicit def catsDataFoldableForEitherT[F[_], L](implicit F: Foldable[F]): Foldable[EitherT[F, L, ?]] = - new EitherTFoldable[F, L] { - val F0: Foldable[F] = F - } - - implicit def catsDataPartialOrderForEitherT[F[_], L, R](implicit F: PartialOrder[F[Either[L, R]]]): PartialOrder[EitherT[F, L, R]] = - new EitherTPartialOrder[F, L, R] { - val F0: PartialOrder[F[Either[L, R]]] = F - } - - implicit def catsDataBitraverseForEitherT[F[_]](implicit F: Traverse[F]): Bitraverse[EitherT[F, ?, ?]] = - new EitherTBitraverse[F] { - val F0: Traverse[F] = F - } -} - -private[data] abstract class EitherTInstances2 extends EitherTInstances3 { - implicit def catsDataMonadErrorForEitherT[F[_], L](implicit F0: Monad[F]): MonadError[EitherT[F, L, ?], L] = - new EitherTMonadError[F, L] { implicit val F = F0 } - - implicit def catsDataRecursiveTailRecMForEitherT[F[_]: RecursiveTailRecM, L]: RecursiveTailRecM[EitherT[F, L, ?]] = - RecursiveTailRecM.create[EitherT[F, L, ?]] - - implicit def catsDataSemigroupKForEitherT[F[_], L](implicit F0: Monad[F]): SemigroupK[EitherT[F, L, ?]] = - new EitherTSemigroupK[F, L] { implicit val F = F0 } - - implicit def catsDataEqForEitherT[F[_], L, R](implicit F: Eq[F[Either[L, R]]]): Eq[EitherT[F, L, R]] = - new EitherTEq[F, L, R] { - val F0: Eq[F[Either[L, R]]] = F - } -} - -private[data] abstract class EitherTInstances3 { - implicit def catsDataFunctorForEitherT[F[_], L](implicit F0: Functor[F]): Functor[EitherT[F, L, ?]] = - new EitherTFunctor[F, L] { implicit val F = F0 } -} - -private[data] trait EitherTSemigroup[F[_], L, A] extends Semigroup[EitherT[F, L, A]] { - implicit val F0: Semigroup[F[Either[L, A]]] - def combine(x: EitherT[F, L , A], y: EitherT[F, L , A]): EitherT[F, L , A] = - EitherT(F0.combine(x.value, y.value)) -} - -private[data] trait EitherTMonoid[F[_], L, A] extends Monoid[EitherT[F, L, A]] with EitherTSemigroup[F, L, A] { - implicit val F0: Monoid[F[Either[L, A]]] - def empty: EitherT[F, L, A] = EitherT(F0.empty) -} - -private[data] trait EitherTSemigroupK[F[_], L] extends SemigroupK[EitherT[F, L, ?]] { - implicit val F: Monad[F] - def combineK[A](x: EitherT[F, L, A], y: EitherT[F, L, A]): EitherT[F, L, A] = - EitherT(F.flatMap(x.value) { - case l @ Left(_) => y.value - case r @ Right(_) => F.pure(r) - }) -} - -private[data] trait EitherTFunctor[F[_], L] extends Functor[EitherT[F, L, ?]] { - implicit val F: Functor[F] - override def map[A, B](fa: EitherT[F, L, A])(f: A => B): EitherT[F, L, B] = fa map f -} - -private[data] trait EitherTMonad[F[_], L] extends Monad[EitherT[F, L, ?]] with EitherTFunctor[F, L] { - implicit val F: Monad[F] - def pure[A](a: A): EitherT[F, L, A] = EitherT(F.pure(Either.right(a))) - def flatMap[A, B](fa: EitherT[F, L, A])(f: A => EitherT[F, L, B]): EitherT[F, L, B] = fa flatMap f - def tailRecM[A, B](a: A)(f: A => EitherT[F, L, Either[A, B]]): EitherT[F, L, B] = - EitherT(F.tailRecM(a)(a0 => F.map(f(a0).value) { - case Left(l) => Right(Left(l)) - case Right(Left(a1)) => Left(a1) - case Right(Right(b)) => Right(Right(b)) - })) -} - -private[data] trait EitherTMonadError[F[_], L] extends MonadError[EitherT[F, L, ?], L] with EitherTMonad[F, L] { - def handleErrorWith[A](fea: EitherT[F, L, A])(f: L => EitherT[F, L, A]): EitherT[F, L, A] = - EitherT(F.flatMap(fea.value) { - case Left(e) => f(e).value - case r @ Right(_) => F.pure(r) - }) - override def handleError[A](fea: EitherT[F, L, A])(f: L => A): EitherT[F, L, A] = - EitherT(F.flatMap(fea.value) { - case Left(e) => F.pure(Right(f(e))) - case r @ Right(_) => F.pure(r) - }) - def raiseError[A](e: L): EitherT[F, L, A] = EitherT.left(F.pure(e)) - override def attempt[A](fla: EitherT[F, L, A]): EitherT[F, L, Either[L, A]] = EitherT.right(fla.value) - override def recover[A](fla: EitherT[F, L, A])(pf: PartialFunction[L, A]): EitherT[F, L, A] = - fla.recover(pf) - override def recoverWith[A](fla: EitherT[F, L, A])(pf: PartialFunction[L, EitherT[F, L, A]]): EitherT[F, L, A] = - fla.recoverWith(pf) -} - -private[data] trait EitherTMonadFilter[F[_], L] extends MonadFilter[EitherT[F, L, ?]] with EitherTMonadError[F, L] { - implicit val F: Monad[F] - implicit val L: Monoid[L] - def empty[A]: EitherT[F, L, A] = EitherT(F.pure(Either.left(L.empty))) -} - -/* TODO violates right absorbtion, right distributivity, and left distributivity -- re-enable when MonadCombine laws are split in to weak/strong -private[data] trait EitherTMonadCombine[F[_], L] extends MonadCombine[EitherT[F, L, ?]] with EitherTMonadFilter[F, L] with EitherTSemigroupK[F, L] { - implicit val F: Monad[F] - implicit val L: Monoid[L] -} -*/ - -private[data] sealed trait EitherTFoldable[F[_], L] extends Foldable[EitherT[F, L, ?]] { - implicit def F0: Foldable[F] - - def foldLeft[A, B](fa: EitherT[F, L, A], b: B)(f: (B, A) => B): B = - fa.foldLeft(b)(f) - - def foldRight[A, B](fa: EitherT[F, L, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = - fa.foldRight(lb)(f) -} - -private[data] sealed trait EitherTTraverse[F[_], L] extends Traverse[EitherT[F, L, ?]] with EitherTFoldable[F, L] { - override implicit def F0: Traverse[F] - - override def traverse[G[_]: Applicative, A, B](fa: EitherT[F, L, A])(f: A => G[B]): G[EitherT[F, L, B]] = - fa traverse f -} - -private[data] sealed trait EitherTBifoldable[F[_]] extends Bifoldable[EitherT[F, ?, ?]] { - implicit def F0: Foldable[F] - - def bifoldLeft[A, B, C](fab: EitherT[F, A, B], c: C)(f: (C, A) => C, g: (C, B) => C): C = - F0.foldLeft(fab.value, c)( (acc, axb) => Bifoldable[Either].bifoldLeft(axb, acc)(f, g)) - - def bifoldRight[A, B, C](fab: EitherT[F, A, B], c: Eval[C])(f: (A, Eval[C]) => Eval[C], g: (B, Eval[C]) => Eval[C]): Eval[C] = - F0.foldRight(fab.value, c)( (axb, acc) => Bifoldable[Either].bifoldRight(axb, acc)(f, g)) -} - -private[data] sealed trait EitherTBitraverse[F[_]] extends Bitraverse[EitherT[F, ?, ?]] with EitherTBifoldable[F] { - override implicit def F0: Traverse[F] - - override def bitraverse[G[_], A, B, C, D](fab: EitherT[F, A, B])(f: A => G[C], g: B => G[D])(implicit G: Applicative[G]): G[EitherT[F, C, D]] = - fab.bitraverse(f, g) -} - -private[data] sealed trait EitherTEq[F[_], L, A] extends Eq[EitherT[F, L, A]] { - implicit def F0: Eq[F[Either[L, A]]] - - override def eqv(x: EitherT[F, L, A], y: EitherT[F, L, A]): Boolean = x === y -} - -private[data] sealed trait EitherTPartialOrder[F[_], L, A] extends PartialOrder[EitherT[F, L, A]] with EitherTEq[F, L, A]{ - override implicit def F0: PartialOrder[F[Either[L, A]]] - - override def partialCompare(x: EitherT[F, L, A], y: EitherT[F, L, A]): Double = - x partialCompare y -} - -private[data] sealed trait EitherTOrder[F[_], L, A] extends Order[EitherT[F, L, A]] with EitherTPartialOrder[F, L, A]{ - override implicit def F0: Order[F[Either[L, A]]] - - override def compare(x: EitherT[F, L, A], y: EitherT[F, L, A]): Int = x compare y -} diff --git a/core/src/main/scala/cats/data/IdT.scala b/core/src/main/scala/cats/data/IdT.scala index 0c619079c0..8dd94d53d4 100644 --- a/core/src/main/scala/cats/data/IdT.scala +++ b/core/src/main/scala/cats/data/IdT.scala @@ -51,7 +51,7 @@ private[data] sealed trait IdTMonad[F[_]] extends Monad[IdT[F, ?]] { def flatMap[A, B](fa: IdT[F, A])(f: A => IdT[F, B]): IdT[F, B] = fa.flatMap(f) - def tailRecM[A, B](a: A)(f: A => IdT[F, Either[A, B]]): IdT[F, B] = + def tailRecM[A, B](a: A)(f: A => IdT[F, Xor[A, B]]): IdT[F, B] = IdT(F0.tailRecM(a)(f(_).value)) } diff --git a/core/src/main/scala/cats/data/Ior.scala b/core/src/main/scala/cats/data/Ior.scala index 88801735e2..bb6fbf82c8 100644 --- a/core/src/main/scala/cats/data/Ior.scala +++ b/core/src/main/scala/cats/data/Ior.scala @@ -11,14 +11,14 @@ import scala.annotation.tailrec * - `[[Ior.Right Right]][B]` * - `[[Ior.Both Both]][A, B]` * - * `A [[Ior]] B` is similar to `Either[A, B]`, except that it can represent the simultaneous presence of + * `A [[Ior]] B` is similar to `Xor[A, B]`, except that it can represent the simultaneous presence of * an `A` and a `B`. It is right-biased so methods such as `map` and `flatMap` operate on the * `B` value. Some methods, like `flatMap`, handle the presence of two [[Ior.Both Both]] values using a * `[[Semigroup]][A]`, while other methods, like [[toEither]], ignore the `A` value in a [[Ior.Both Both]]. * - * `A [[Ior]] B` is isomorphic to `Either[Either[A, B], (A, B)]`, but provides methods biased toward `B` + * `A [[Ior]] B` is isomorphic to `Xor[Xor[A, B], (A, B)]`, but provides methods biased toward `B` * values, regardless of whether the `B` values appear in a [[Ior.Right Right]] or a [[Ior.Both Both]]. - * The isomorphic [[scala.util.Either]] form can be accessed via the [[unwrap]] method. + * The isomorphic [[Xor]] form can be accessed via the [[unwrap]] method. */ sealed abstract class Ior[+A, +B] extends Product with Serializable { @@ -36,10 +36,10 @@ sealed abstract class Ior[+A, +B] extends Product with Serializable { final def right: Option[B] = fold(_ => None, b => Some(b), (_, b) => Some(b)) final def onlyLeft: Option[A] = fold(a => Some(a), _ => None, (_, _) => None) final def onlyRight: Option[B] = fold(_ => None, b => Some(b), (_, _) => None) - final def onlyLeftOrRight: Option[Either[A, B]] = fold(a => Some(Left(a)), b => Some(Right(b)), (_, _) => None) + final def onlyLeftOrRight: Option[A Xor B] = fold(a => Some(Xor.left(a)), b => Some(Xor.right(b)), (_, _) => None) final def onlyBoth: Option[(A, B)] = fold(_ => None, _ => None, (a, b) => Some((a, b))) final def pad: (Option[A], Option[B]) = fold(a => (Some(a), None), b => (None, Some(b)), (a, b) => (Some(a), Some(b))) - final def unwrap: Either[Either[A, B], (A, B)] = fold(a => Left(Left(a)), b => Left(Right(b)), (a, b) => Right((a, b))) + final def unwrap: (A Xor B) Xor (A, B) = fold(a => Xor.left(Xor.left(a)), b => Xor.left(Xor.right(b)), (a, b) => Xor.right((a, b))) final def toXor: A Xor B = fold(Xor.left, Xor.right, (_, b) => Xor.right(b)) final def toEither: Either[A, B] = fold(Left(_), Right(_), (_, b) => Right(b)) @@ -144,14 +144,14 @@ private[data] sealed abstract class IorInstances extends IorInstances0 { implicit def catsDataMonadForIor[A: Semigroup]: Monad[A Ior ?] with RecursiveTailRecM[A Ior ?] = new Monad[A Ior ?] with RecursiveTailRecM[A Ior ?] { def pure[B](b: B): A Ior B = Ior.right(b) def flatMap[B, C](fa: A Ior B)(f: B => A Ior C): A Ior C = fa.flatMap(f) - def tailRecM[B, C](b: B)(fn: B => Ior[A, Either[B, C]]): A Ior C = { + def tailRecM[B, C](b: B)(fn: B => Ior[A, Xor[B, C]]): A Ior C = { @tailrec - def loop(v: Ior[A, Either[B, C]]): A Ior C = v match { + def loop(v: Ior[A, Xor[B, C]]): A Ior C = v match { case Ior.Left(a) => Ior.left(a) - case Ior.Right(Right(c)) => Ior.right(c) - case Ior.Both(a, Right(c)) => Ior.both(a, c) - case Ior.Right(Left(b)) => loop(fn(b)) - case Ior.Both(a, Left(b)) => + case Ior.Right(Xor.Right(c)) => Ior.right(c) + case Ior.Both(a, Xor.Right(c)) => Ior.both(a, c) + case Ior.Right(Xor.Left(b)) => loop(fn(b)) + case Ior.Both(a, Xor.Left(b)) => fn(b) match { case Ior.Left(aa) => Ior.left(Semigroup[A].combine(a, aa)) case Ior.Both(aa, x) => loop(Ior.both(Semigroup[A].combine(a, aa), x)) diff --git a/core/src/main/scala/cats/data/Kleisli.scala b/core/src/main/scala/cats/data/Kleisli.scala index f1fbcb07c1..66bc4340a8 100644 --- a/core/src/main/scala/cats/data/Kleisli.scala +++ b/core/src/main/scala/cats/data/Kleisli.scala @@ -101,7 +101,7 @@ private[data] sealed abstract class KleisliInstances extends KleisliInstances0 { new Choice[Kleisli[F, ?, ?]] { def id[A]: Kleisli[F, A, A] = Kleisli(ev.pure(_)) - def choice[A, B, C](f: Kleisli[F, A, C], g: Kleisli[F, B, C]): Kleisli[F, Either[A, B], C] = + def choice[A, B, C](f: Kleisli[F, A, C], g: Kleisli[F, B, C]): Kleisli[F, Xor[A, B], C] = Kleisli(_.fold(f.run, g.run)) def compose[A, B, C](f: Kleisli[F, B, C], g: Kleisli[F, A, B]): Kleisli[F, A, C] = @@ -145,7 +145,7 @@ private[data] sealed abstract class KleisliInstances0 extends KleisliInstances1 def map[B, C](fa: Kleisli[F, A, B])(f: B => C): Kleisli[F, A, C] = fa.map(f) - def tailRecM[B, C](b: B)(f: B => Kleisli[F, A, Either[B, C]]): Kleisli[F, A, C] = + def tailRecM[B, C](b: B)(f: B => Kleisli[F, A, Xor[B, C]]): Kleisli[F, A, C] = Kleisli[F, A, C]({ a => FlatMap[F].tailRecM(b) { f(_).run(a) } }) } @@ -201,7 +201,7 @@ private[data] sealed abstract class KleisliInstances4 { def local[B](f: A => A)(fa: Kleisli[F, A, B]): Kleisli[F, A, B] = Kleisli(f.andThen(fa.run)) - def tailRecM[B, C](b: B)(f: B => Kleisli[F, A, Either[B, C]]): Kleisli[F, A, C] = + def tailRecM[B, C](b: B)(f: B => Kleisli[F, A, Xor[B, C]]): Kleisli[F, A, C] = Kleisli[F, A, C]({ a => FlatMap[F].tailRecM(b) { f(_).run(a) } }) } } diff --git a/core/src/main/scala/cats/data/NonEmptyList.scala b/core/src/main/scala/cats/data/NonEmptyList.scala index 9aaffa7bde..ccb571422a 100644 --- a/core/src/main/scala/cats/data/NonEmptyList.scala +++ b/core/src/main/scala/cats/data/NonEmptyList.scala @@ -196,16 +196,16 @@ private[data] sealed trait NonEmptyListInstances extends NonEmptyListInstances0 override def foldRight[A, B](fa: NonEmptyList[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = fa.foldRight(lb)(f) - def tailRecM[A, B](a: A)(f: A => NonEmptyList[Either[A, B]]): NonEmptyList[B] = { + def tailRecM[A, B](a: A)(f: A => NonEmptyList[A Xor B]): NonEmptyList[B] = { val buf = new ListBuffer[B] - @tailrec def go(v: NonEmptyList[Either[A, B]]): Unit = v.head match { - case Right(b) => + @tailrec def go(v: NonEmptyList[A Xor B]): Unit = v.head match { + case Xor.Right(b) => buf += b NonEmptyList.fromList(v.tail) match { case Some(t) => go(t) case None => () } - case Left(a) => go(f(a) ++ v.tail) + case Xor.Left(a) => go(f(a) ++ v.tail) } go(f(a)) NonEmptyList.fromListUnsafe(buf.result()) diff --git a/core/src/main/scala/cats/data/NonEmptyVector.scala b/core/src/main/scala/cats/data/NonEmptyVector.scala index fb6824d664..6da76beaa8 100644 --- a/core/src/main/scala/cats/data/NonEmptyVector.scala +++ b/core/src/main/scala/cats/data/NonEmptyVector.scala @@ -196,16 +196,16 @@ private[data] sealed trait NonEmptyVectorInstances { override def foldRight[A, B](fa: NonEmptyVector[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = fa.foldRight(lb)(f) - def tailRecM[A, B](a: A)(f: A => NonEmptyVector[Either[A, B]]): NonEmptyVector[B] = { + def tailRecM[A, B](a: A)(f: A => NonEmptyVector[A Xor B]): NonEmptyVector[B] = { val buf = new VectorBuilder[B] - @tailrec def go(v: NonEmptyVector[Either[A, B]]): Unit = v.head match { - case Right(b) => + @tailrec def go(v: NonEmptyVector[A Xor B]): Unit = v.head match { + case Xor.Right(b) => buf += b NonEmptyVector.fromVector(v.tail) match { case Some(t) => go(t) case None => () } - case Left(a) => go(f(a).concat(v.tail)) + case Xor.Left(a) => go(f(a).concat(v.tail)) } go(f(a)) NonEmptyVector.fromVectorUnsafe(buf.result()) diff --git a/core/src/main/scala/cats/data/OneAnd.scala b/core/src/main/scala/cats/data/OneAnd.scala index ee50730ebb..937130a5cf 100644 --- a/core/src/main/scala/cats/data/OneAnd.scala +++ b/core/src/main/scala/cats/data/OneAnd.scala @@ -137,14 +137,14 @@ private[data] sealed trait OneAndInstances extends OneAndLowPriority2 { OneAnd(fst.head, monad.combineK(fst.tail, end)) } - def tailRecM[A, B](a: A)(fn: A => OneAnd[F, Either[A, B]]): OneAnd[F, B] = { - def stepF(a: A): F[Either[A, B]] = { + def tailRecM[A, B](a: A)(fn: A => OneAnd[F, Xor[A, B]]): OneAnd[F, B] = { + def stepF(a: A): F[Xor[A, B]] = { val oneAnd = fn(a) monad.combineK(monad.pure(oneAnd.head), oneAnd.tail) } - def toFB(in: Either[A, B]): F[B] = in match { - case Right(b) => monad.pure(b) - case Left(a) => monad.tailRecM(a)(stepF) + def toFB(in: Xor[A, B]): F[B] = in match { + case Xor.Right(b) => monad.pure(b) + case Xor.Left(a) => monad.tailRecM(a)(stepF) } // This could probably be in SemigroupK to perform well @@ -158,10 +158,10 @@ private[data] sealed trait OneAndInstances extends OneAndLowPriority2 { @tailrec def go(in: A, rest: List[F[B]]): OneAnd[F, B] = fn(in) match { - case OneAnd(Right(b), tail) => + case OneAnd(Xor.Right(b), tail) => val fbs = monad.flatMap(tail)(toFB) OneAnd(b, combineAll(fbs :: rest)) - case OneAnd(Left(a), tail) => + case OneAnd(Xor.Left(a), tail) => val fbs = monad.flatMap(tail)(toFB) go(a, fbs :: rest) } diff --git a/core/src/main/scala/cats/data/OptionT.scala b/core/src/main/scala/cats/data/OptionT.scala index 65c42c4634..b754222fec 100644 --- a/core/src/main/scala/cats/data/OptionT.scala +++ b/core/src/main/scala/cats/data/OptionT.scala @@ -1,8 +1,7 @@ package cats package data -import cats.instances.option.{catsStdInstancesForOption => optionInstance} -import cats.syntax.either._ +import instances.option.{catsStdInstancesForOption => optionInstance} /** * `OptionT[F[_], A]` is a light wrapper on an `F[Option[A]]` with some @@ -86,11 +85,11 @@ final case class OptionT[F[_], A](value: F[Option[A]]) { case None => default }) - def toRight[L](left: => L)(implicit F: Functor[F]): EitherT[F, L, A] = - EitherT(cata(Left(left), Right.apply)) + def toRight[L](left: => L)(implicit F: Functor[F]): XorT[F, L, A] = + XorT(cata(Xor.Left(left), Xor.Right.apply)) - def toLeft[R](right: => R)(implicit F: Functor[F]): EitherT[F, A, R] = - EitherT(cata(Right(right), Left.apply)) + def toLeft[R](right: => R)(implicit F: Functor[F]): XorT[F, A, R] = + XorT(cata(Xor.Right(right), Xor.Left.apply)) def show(implicit F: Show[F[Option[A]]]): String = F.show(value) @@ -252,9 +251,9 @@ private[data] trait OptionTMonad[F[_]] extends Monad[OptionT[F, ?]] { override def map[A, B](fa: OptionT[F, A])(f: A => B): OptionT[F, B] = fa.map(f) - def tailRecM[A, B](a: A)(f: A => OptionT[F, Either[A, B]]): OptionT[F, B] = + def tailRecM[A, B](a: A)(f: A => OptionT[F, A Xor B]): OptionT[F, B] = OptionT(F.tailRecM(a)(a0 => F.map(f(a0).value)( - _.fold(Either.right[A, Option[B]](None))(_.map(b => Some(b): Option[B])) + _.fold(Xor.right[A, Option[B]](None))(_.map(Some(_))) ))) } diff --git a/core/src/main/scala/cats/data/StateT.scala b/core/src/main/scala/cats/data/StateT.scala index 75b0c10401..11d8732c7d 100644 --- a/core/src/main/scala/cats/data/StateT.scala +++ b/core/src/main/scala/cats/data/StateT.scala @@ -1,8 +1,6 @@ package cats package data -import cats.syntax.either._ - /** * `StateT[F, S, A]` is similar to `Kleisli[F, S, A]` in that it takes an `S` * argument and produces an `A` value wrapped in `F`. However, it also produces @@ -219,7 +217,7 @@ private[data] sealed trait StateTMonad[F[_], S] extends Monad[StateT[F, S, ?]] { override def map[A, B](fa: StateT[F, S, A])(f: A => B): StateT[F, S, B] = fa.map(f) - def tailRecM[A, B](a: A)(f: A => StateT[F, S, Either[A, B]]): StateT[F, S, B] = + def tailRecM[A, B](a: A)(f: A => StateT[F, S, A Xor B]): StateT[F, S, B] = StateT[F, S, B](s => F.tailRecM[(S, A), (S, B)]((s, a)) { case (s, a) => F.map(f(a).run(s)) { case (s, ab) => ab.bimap((s, _), (s, _)) } }) diff --git a/core/src/main/scala/cats/data/Validated.scala b/core/src/main/scala/cats/data/Validated.scala index da67b00d4d..cce53e1a74 100644 --- a/core/src/main/scala/cats/data/Validated.scala +++ b/core/src/main/scala/cats/data/Validated.scala @@ -80,11 +80,11 @@ sealed abstract class Validated[+E, +A] extends Product with Serializable { def toXor: Xor[E, A] = fold(Xor.Left.apply, Xor.Right.apply) /** - * Convert to an Either, apply a function, convert back. This is handy - * when you want to use the Monadic properties of the Either type. + * Convert to an Xor, apply a function, convert back. This is handy + * when you want to use the Monadic properties of the Xor type. */ - def withEither[EE, B](f: Either[E, A] => Either[EE, B]): Validated[EE, B] = - Validated.fromEither(f(toEither)) + def withXor[EE, B](f: (E Xor A) => (EE Xor B)): Validated[EE, B] = + f(toXor).toValidated /** * Validated is a [[functor.Bifunctor]], this method applies one of the @@ -177,7 +177,7 @@ sealed abstract class Validated[+E, +A] extends Product with Serializable { * This allows "chained" validation: the output of one validation can be fed * into another validation function. * - * This function is similar to `flatMap` on `Either`. It's not called `flatMap`, + * This function is similar to `Xor.flatMap`. It's not called `flatMap`, * because by Cats convention, `flatMap` is a monadic bind that is consistent * with `ap`. This method is not consistent with [[ap]] (or other * `Apply`-based methods), because it has "fail-fast" behavior as opposed to diff --git a/core/src/main/scala/cats/data/WriterT.scala b/core/src/main/scala/cats/data/WriterT.scala index f0c683b595..5f8aaa5747 100644 --- a/core/src/main/scala/cats/data/WriterT.scala +++ b/core/src/main/scala/cats/data/WriterT.scala @@ -248,17 +248,17 @@ private[data] sealed trait WriterTFlatMap1[F[_], L] extends WriterTApply[F, L] w def flatMap[A, B](fa: WriterT[F, L, A])(f: A => WriterT[F, L, B]): WriterT[F, L, B] = fa.flatMap(f) - def tailRecM[A, B](a: A)(fn: A => WriterT[F, L, Either[A, B]]): WriterT[F, L, B] = { + def tailRecM[A, B](a: A)(fn: A => WriterT[F, L, Xor[A, B]]): WriterT[F, L, B] = { - def step(la: (L, A)): F[Either[(L, A), (L, B)]] = { - val flv: F[(L, Either[A, B])] = fn(la._2).run + def step(la: (L, A)): F[Xor[(L, A), (L, B)]] = { + val flv: F[(L, Xor[A, B])] = fn(la._2).run F0.map(flv) { - case (l, Left(a)) => + case (l, Xor.Left(a)) => val combineL = L0.combine(la._1, l) - Left((combineL, a)) - case (l, Right(b)) => + Xor.Left((combineL, a)) + case (l, Xor.Right(b)) => val combineL = L0.combine(la._1, l) - Right((combineL, b)) + Xor.Right((combineL, b)) } } @@ -273,24 +273,24 @@ private[data] sealed trait WriterTFlatMap2[F[_], L] extends WriterTApply[F, L] w def flatMap[A, B](fa: WriterT[F, L, A])(f: A => WriterT[F, L, B]): WriterT[F, L, B] = fa.flatMap(f) - def tailRecM[A, B](a: A)(fn: A => WriterT[F, L, Either[A, B]]): WriterT[F, L, B] = { + def tailRecM[A, B](a: A)(fn: A => WriterT[F, L, Xor[A, B]]): WriterT[F, L, B] = { - def step(la: (L, A)): F[Either[(L, A), (L, B)]] = { - val flv: F[(L, Either[A, B])] = fn(la._2).run + def step(la: (L, A)): F[Xor[(L, A), (L, B)]] = { + val flv: F[(L, Xor[A, B])] = fn(la._2).run F0.map(flv) { - case (l, Left(a)) => + case (l, Xor.Left(a)) => val combineL = L0.combine(la._1, l) - Left((combineL, a)) - case (l, Right(b)) => + Xor.Left((combineL, a)) + case (l, Xor.Right(b)) => val combineL = L0.combine(la._1, l) - Right((combineL, b)) + Xor.Right((combineL, b)) } } val init = fn(a).run val res: F[(L, B)] = F0.flatMap(init) { - case (l, Right(b)) => F0.pure((l, b)) - case (l, Left(a)) => F0.tailRecM((l, a))(step) + case (l, Xor.Right(b)) => F0.pure((l, b)) + case (l, Xor.Left(a)) => F0.tailRecM((l, a))(step) } WriterT(res) } diff --git a/core/src/main/scala/cats/data/Xor.scala b/core/src/main/scala/cats/data/Xor.scala index 077d58be73..8fc366d9dd 100644 --- a/core/src/main/scala/cats/data/Xor.scala +++ b/core/src/main/scala/cats/data/Xor.scala @@ -263,11 +263,11 @@ private[data] sealed abstract class XorInstances extends XorInstances1 { def flatMap[B, C](fa: A Xor B)(f: B => A Xor C): A Xor C = fa.flatMap(f) override def ap[B, C](x: A Xor (B => C))(y: A Xor B): A Xor C = y.ap(x) def pure[B](b: B): A Xor B = Xor.right(b) - @tailrec def tailRecM[B, C](b: B)(f: B => A Xor Either[B, C]): A Xor C = + @tailrec def tailRecM[B, C](b: B)(f: B => A Xor (B Xor C)): A Xor C = f(b) match { case Xor.Left(a) => Xor.Left(a) - case Xor.Right(Left(b1)) => tailRecM(b1)(f) - case Xor.Right(Right(c)) => Xor.Right(c) + case Xor.Right(Xor.Left(b1)) => tailRecM(b1)(f) + case Xor.Right(Xor.Right(c)) => Xor.Right(c) } def handleErrorWith[B](fea: Xor[A, B])(f: A => Xor[A, B]): Xor[A, B] = fea match { @@ -278,7 +278,7 @@ private[data] sealed abstract class XorInstances extends XorInstances1 { override def map[B, C](fa: A Xor B)(f: B => C): A Xor C = fa.map(f) override def map2Eval[B, C, Z](fb: A Xor B, fc: Eval[A Xor C])(f: (B, C) => Z): Eval[A Xor Z] = fb.map2Eval(fc)(f) - override def attempt[B](fab: A Xor B): A Xor (Either[A, B]) = Xor.right(fab.toEither) + override def attempt[B](fab: A Xor B): A Xor (A Xor B) = Xor.right(fab) override def recover[B](fab: A Xor B)(pf: PartialFunction[A, B]): A Xor B = fab recover pf override def recoverWith[B](fab: A Xor B)(pf: PartialFunction[A, A Xor B]): A Xor B = diff --git a/core/src/main/scala/cats/data/XorT.scala b/core/src/main/scala/cats/data/XorT.scala index 198d902e6b..ce2afe23cb 100644 --- a/core/src/main/scala/cats/data/XorT.scala +++ b/core/src/main/scala/cats/data/XorT.scala @@ -55,8 +55,6 @@ final case class XorT[F[_], A, B](value: F[A Xor B]) { def toEither(implicit F: Functor[F]): F[Either[A, B]] = F.map(value)(_.toEither) - def toEitherT(implicit F: Functor[F]): EitherT[F, A, B] = EitherT(toEither) - def toOption(implicit F: Functor[F]): OptionT[F, B] = OptionT(F.map(value)(_.toOption)) def to[G[_]](implicit F: Functor[F], G: Alternative[G]): F[G[B]] = @@ -383,11 +381,11 @@ private[data] trait XorTMonad[F[_], L] extends Monad[XorT[F, L, ?]] with XorTFun implicit val F: Monad[F] def pure[A](a: A): XorT[F, L, A] = XorT(F.pure(Xor.right(a))) def flatMap[A, B](fa: XorT[F, L, A])(f: A => XorT[F, L, B]): XorT[F, L, B] = fa flatMap f - def tailRecM[A, B](a: A)(f: A => XorT[F, L, Either[A, B]]): XorT[F, L, B] = + def tailRecM[A, B](a: A)(f: A => XorT[F, L, Xor[A, B]]): XorT[F, L, B] = XorT(F.tailRecM(a)(a0 => F.map(f(a0).value) { - case Xor.Left(l) => Right(Xor.Left(l)) - case Xor.Right(Left(a1)) => Left(a1) - case Xor.Right(Right(b)) => Right(Xor.Right(b)) + case Xor.Left(l) => Xor.Right(Xor.Left(l)) + case Xor.Right(Xor.Left(a1)) => Xor.Left(a1) + case Xor.Right(Xor.Right(b)) => Xor.Right(Xor.Right(b)) })) } @@ -403,7 +401,7 @@ private[data] trait XorTMonadError[F[_], L] extends MonadError[XorT[F, L, ?], L] case r @ Xor.Right(_) => F.pure(r) }) def raiseError[A](e: L): XorT[F, L, A] = XorT.left(F.pure(e)) - override def attempt[A](fla: XorT[F, L, A]): XorT[F, L, Either[L, A]] = XorT.right(fla.toEither) + override def attempt[A](fla: XorT[F, L, A]): XorT[F, L, L Xor A] = XorT.right(fla.value) override def recover[A](fla: XorT[F, L, A])(pf: PartialFunction[L, A]): XorT[F, L, A] = fla.recover(pf) override def recoverWith[A](fla: XorT[F, L, A])(pf: PartialFunction[L, XorT[F, L, A]]): XorT[F, L, A] = diff --git a/core/src/main/scala/cats/functor/Bifunctor.scala b/core/src/main/scala/cats/functor/Bifunctor.scala index c0140d6f34..0d23fc9177 100644 --- a/core/src/main/scala/cats/functor/Bifunctor.scala +++ b/core/src/main/scala/cats/functor/Bifunctor.scala @@ -41,11 +41,12 @@ import simulacrum.typeclass * Widens A into a supertype AA. * Example: * {{{ + * scala> import cats.data.Xor * scala> import cats.implicits._ * scala> sealed trait Foo * scala> case object Bar extends Foo - * scala> val x1: Either[Bar.type, Int] = Either.left(Bar) - * scala> val x2: Either[Foo, Int] = x1.leftWiden + * scala> val x1: Xor[Bar.type, Int] = Xor.left(Bar) + * scala> val x2: Xor[Foo, Int] = x1.leftWiden * }}} */ def leftWiden[A, B, AA >: A](fab: F[A, B]): F[AA, B] = fab.asInstanceOf[F[AA, B]] diff --git a/core/src/main/scala/cats/instances/either.scala b/core/src/main/scala/cats/instances/either.scala index d5b3cd628f..db3563c204 100644 --- a/core/src/main/scala/cats/instances/either.scala +++ b/core/src/main/scala/cats/instances/either.scala @@ -1,8 +1,8 @@ package cats package instances -import cats.syntax.EitherUtil -import cats.syntax.either._ +import cats.data.Xor + import scala.annotation.tailrec trait EitherInstances extends EitherInstances1 { @@ -27,7 +27,6 @@ trait EitherInstances extends EitherInstances1 { } } - // scalastyle:off method.length implicit def catsStdInstancesForEither[A]: MonadError[Either[A, ?], A] with Traverse[Either[A, ?]] with RecursiveTailRecM[Either[A, ?]] = new MonadError[Either[A, ?], A] with Traverse[Either[A, ?]] with RecursiveTailRecM[Either[A, ?]] { def pure[B](b: B): Either[A, B] = Right(b) @@ -46,16 +45,16 @@ trait EitherInstances extends EitherInstances1 { fa.right.map(f) @tailrec - def tailRecM[B, C](b: B)(f: B => Either[A, Either[B, C]]): Either[A, C] = + def tailRecM[B, C](b: B)(f: B => Either[A, Xor[B, C]]): Either[A, C] = f(b) match { - case Left(a) => Left(a) - case Right(Left(b1)) => tailRecM(b1)(f) - case Right(Right(c)) => Right(c) + case Left(a) => Left(a) + case Right(Xor.Left(b1)) => tailRecM(b1)(f) + case Right(Xor.Right(c)) => Right(c) } override def map2Eval[B, C, Z](fb: Either[A, B], fc: Eval[Either[A, C]])(f: (B, C) => Z): Eval[Either[A, Z]] = fb match { - case l @ Left(_) => Now(EitherUtil.rightCast(l)) + case l @ Left(_) => Now(l.asInstanceOf[Either[A, Z]]) case Right(b) => fc.map(_.right.map(f(b, _))) } @@ -70,16 +69,7 @@ trait EitherInstances extends EitherInstances1 { def foldRight[B, C](fa: Either[A, B], lc: Eval[C])(f: (B, Eval[C]) => Eval[C]): Eval[C] = fa.fold(_ => lc, b => f(b, lc)) - - override def attempt[B](fab: Either[A, B]): Either[A, Either[A, B]] = Right(fab) - override def recover[B](fab: Either[A, B])(pf: PartialFunction[A, B]): Either[A, B] = - fab recover pf - override def recoverWith[B](fab: Either[A, B])(pf: PartialFunction[A, Either[A, B]]): Either[A, B] = - fab recoverWith pf - override def ensure[B](fab: Either[A, B])(error: => A)(predicate: B => Boolean): Either[A, B] = - fab.ensure(error)(predicate) } - // scalastyle:on method.length implicit def catsStdOrderForEither[A, B](implicit A: Order[A], B: Order[B]): Order[Either[A, B]] = new Order[Either[A, B]] { def compare(x: Either[A, B], y: Either[A, B]): Int = x.fold( @@ -95,20 +85,6 @@ trait EitherInstances extends EitherInstances1 { b => s"Right(${B.show(b)})" ) } - - implicit def catsDataMonoidForEither[A, B](implicit B: Monoid[B]): Monoid[Either[A, B]] = - new Monoid[Either[A, B]] { - def empty: Either[A, B] = Right(B.empty) - def combine(x: Either[A, B], y: Either[A, B]): Either[A, B] = x combine y - } - - implicit def catsDataSemigroupKForEither[L]: SemigroupK[Either[L, ?]] = - new SemigroupK[Either[L, ?]] { - def combineK[A](x: Either[L, A], y: Either[L, A]): Either[L, A] = x match { - case Left(_) => y - case Right(_) => x - } - } } private[instances] sealed trait EitherInstances1 extends EitherInstances2 { diff --git a/core/src/main/scala/cats/instances/function.scala b/core/src/main/scala/cats/instances/function.scala index bf01a45fe9..530623857c 100644 --- a/core/src/main/scala/cats/instances/function.scala +++ b/core/src/main/scala/cats/instances/function.scala @@ -2,6 +2,7 @@ package cats package instances import cats.arrow.{Arrow, Choice} +import cats.data.Xor import cats.functor.Contravariant import annotation.tailrec @@ -19,12 +20,12 @@ private[instances] sealed trait Function0Instances { def flatMap[A, B](fa: () => A)(f: A => () => B): () => B = () => f(fa())() - def tailRecM[A, B](a: A)(fn: A => () => Either[A, B]): () => B = + def tailRecM[A, B](a: A)(fn: A => () => Xor[A, B]): () => B = () => { @tailrec def loop(thisA: A): B = fn(thisA)() match { - case Right(b) => b - case Left(nextA) => loop(nextA) + case Xor.Right(b) => b + case Xor.Left(nextA) => loop(nextA) } loop(a) } @@ -57,12 +58,12 @@ private[instances] sealed trait Function1Instances extends Function1Instances0 { override def map[R1, R2](fa: T1 => R1)(f: R1 => R2): T1 => R2 = f.compose(fa) - def tailRecM[A, B](a: A)(fn: A => T1 => Either[A, B]): T1 => B = + def tailRecM[A, B](a: A)(fn: A => T1 => Xor[A, B]): T1 => B = (t: T1) => { @tailrec def step(thisA: A): B = fn(thisA)(t) match { - case Right(b) => b - case Left(nextA) => step(nextA) + case Xor.Right(b) => b + case Xor.Left(nextA) => step(nextA) } step(a) } @@ -70,10 +71,10 @@ private[instances] sealed trait Function1Instances extends Function1Instances0 { implicit val catsStdInstancesForFunction1: Choice[Function1] with Arrow[Function1] = new Choice[Function1] with Arrow[Function1] { - def choice[A, B, C](f: A => C, g: B => C): Either[A, B] => C = + def choice[A, B, C](f: A => C, g: B => C): Xor[A, B] => C = _ match { - case Left(a) => f(a) - case Right(b) => g(b) + case Xor.Left(a) => f(a) + case Xor.Right(b) => g(b) } def lift[A, B](f: A => B): A => B = f diff --git a/core/src/main/scala/cats/instances/future.scala b/core/src/main/scala/cats/instances/future.scala index 216a56c426..750f7e5bb1 100644 --- a/core/src/main/scala/cats/instances/future.scala +++ b/core/src/main/scala/cats/instances/future.scala @@ -1,6 +1,8 @@ package cats package instances +import cats.data.Xor + import scala.util.control.NonFatal import scala.concurrent.{ExecutionContext, Future} @@ -16,10 +18,10 @@ trait FutureInstances extends FutureInstances1 { * Note that while this implementation will not compile with `@tailrec`, * it is in fact stack-safe. */ - final def tailRecM[B, C](b: B)(f: B => Future[Either[B, C]]): Future[C] = + final def tailRecM[B, C](b: B)(f: B => Future[(B Xor C)]): Future[C] = f(b).flatMap { - case Left(b1) => tailRecM(b1)(f) - case Right(c) => Future.successful(c) + case Xor.Left(b1) => tailRecM(b1)(f) + case Xor.Right(c) => Future.successful(c) } def handleErrorWith[A](fea: Future[A])(f: Throwable => Future[A]): Future[A] = fea.recoverWith { case t => f(t) } @@ -27,8 +29,8 @@ trait FutureInstances extends FutureInstances1 { def raiseError[A](e: Throwable): Future[A] = Future.failed(e) override def handleError[A](fea: Future[A])(f: Throwable => A): Future[A] = fea.recover { case t => f(t) } - override def attempt[A](fa: Future[A]): Future[Either[Throwable, A]] = - (fa.map(a => Right[Throwable, A](a))) recover { case NonFatal(t) => Left(t) } + override def attempt[A](fa: Future[A]): Future[Throwable Xor A] = + (fa map Xor.right) recover { case NonFatal(t) => Xor.left(t) } override def recover[A](fa: Future[A])(pf: PartialFunction[Throwable, A]): Future[A] = fa.recover(pf) diff --git a/core/src/main/scala/cats/instances/list.scala b/core/src/main/scala/cats/instances/list.scala index cfbfc7cf0d..7b70549a37 100644 --- a/core/src/main/scala/cats/instances/list.scala +++ b/core/src/main/scala/cats/instances/list.scala @@ -6,6 +6,8 @@ import cats.syntax.show._ import scala.annotation.tailrec import scala.collection.mutable.ListBuffer +import cats.data.Xor + trait ListInstances extends cats.kernel.instances.ListInstances { implicit val catsStdInstancesForList: TraverseFilter[List] with MonadCombine[List] with Monad[List] with CoflatMap[List] with RecursiveTailRecM[List] = @@ -26,12 +28,12 @@ trait ListInstances extends cats.kernel.instances.ListInstances { override def map2[A, B, Z](fa: List[A], fb: List[B])(f: (A, B) => Z): List[Z] = fa.flatMap(a => fb.map(b => f(a, b))) - def tailRecM[A, B](a: A)(f: A => List[Either[A, B]]): List[B] = { + def tailRecM[A, B](a: A)(f: A => List[A Xor B]): List[B] = { val buf = List.newBuilder[B] - @tailrec def go(lists: List[List[Either[A, B]]]): Unit = lists match { + @tailrec def go(lists: List[List[A Xor B]]): Unit = lists match { case (ab :: abs) :: tail => ab match { - case Right(b) => buf += b; go(abs :: tail) - case Left(a) => go(f(a) :: abs :: tail) + case Xor.Right(b) => buf += b; go(abs :: tail) + case Xor.Left(a) => go(f(a) :: abs :: tail) } case Nil :: tail => go(tail) case Nil => () diff --git a/core/src/main/scala/cats/instances/map.scala b/core/src/main/scala/cats/instances/map.scala index 166f33b9c3..fbe021f7e4 100644 --- a/core/src/main/scala/cats/instances/map.scala +++ b/core/src/main/scala/cats/instances/map.scala @@ -1,6 +1,8 @@ package cats package instances +import cats.data.Xor + import scala.annotation.tailrec trait MapInstances extends cats.kernel.instances.MapInstances { @@ -56,17 +58,17 @@ trait MapInstances extends cats.kernel.instances.MapInstances { def foldRight[A, B](fa: Map[K, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = Foldable.iterateRight(fa.values.iterator, lb)(f) - def tailRecM[A, B](a: A)(f: A => Map[K, Either[A, B]]): Map[K, B] = { + def tailRecM[A, B](a: A)(f: A => Map[K, Xor[A, B]]): Map[K, B] = { val bldr = Map.newBuilder[K, B] - @tailrec def descend(k: K, either: Either[A, B]): Unit = + @tailrec def descend(k: K, either: Xor[A, B]): Unit = either match { - case Left(a) => + case Xor.Left(a) => f(a).get(k) match { case Some(x) => descend(k, x) case None => () } - case Right(b) => + case Xor.Right(b) => bldr += ((k, b)) () } diff --git a/core/src/main/scala/cats/instances/option.scala b/core/src/main/scala/cats/instances/option.scala index b9ca152996..c2caaf30d7 100644 --- a/core/src/main/scala/cats/instances/option.scala +++ b/core/src/main/scala/cats/instances/option.scala @@ -2,6 +2,7 @@ package cats package instances import scala.annotation.tailrec +import cats.data.Xor trait OptionInstances extends cats.kernel.instances.OptionInstances { @@ -21,11 +22,11 @@ trait OptionInstances extends cats.kernel.instances.OptionInstances { fa.flatMap(f) @tailrec - def tailRecM[A, B](a: A)(f: A => Option[Either[A, B]]): Option[B] = + def tailRecM[A, B](a: A)(f: A => Option[A Xor B]): Option[B] = f(a) match { case None => None - case Some(Left(a1)) => tailRecM(a1)(f) - case Some(Right(b)) => Some(b) + case Some(Xor.Left(a1)) => tailRecM(a1)(f) + case Some(Xor.Right(b)) => Some(b) } override def map2[A, B, Z](fa: Option[A], fb: Option[B])(f: (A, B) => Z): Option[Z] = diff --git a/core/src/main/scala/cats/instances/stream.scala b/core/src/main/scala/cats/instances/stream.scala index 131170c9bc..1e9ec85b96 100644 --- a/core/src/main/scala/cats/instances/stream.scala +++ b/core/src/main/scala/cats/instances/stream.scala @@ -1,6 +1,8 @@ package cats package instances +import cats.data.Xor + import cats.syntax.show._ import scala.annotation.tailrec @@ -54,38 +56,38 @@ trait StreamInstances extends cats.kernel.instances.StreamInstances { }.value } - def tailRecM[A, B](a: A)(fn: A => Stream[Either[A, B]]): Stream[B] = { + def tailRecM[A, B](a: A)(fn: A => Stream[Xor[A, B]]): Stream[B] = { val it: Iterator[B] = new Iterator[B] { - var stack: Stream[Either[A, B]] = fn(a) - var state: Either[Unit, Option[B]] = Left(()) + var stack: Stream[Xor[A, B]] = fn(a) + var state: Xor[Unit, Option[B]] = Xor.Left(()) @tailrec def advance(): Unit = stack match { - case Right(b) #:: tail => + case Xor.Right(b) #:: tail => stack = tail - state = Right(Some(b)) - case Left(a) #:: tail => + state = Xor.Right(Some(b)) + case Xor.Left(a) #:: tail => stack = fn(a) #::: tail advance case empty => - state = Right(None) + state = Xor.Right(None) } @tailrec def hasNext: Boolean = state match { - case Left(()) => + case Xor.Left(()) => advance() hasNext - case Right(o) => + case Xor.Right(o) => o.isDefined } @tailrec def next(): B = state match { - case Left(()) => + case Xor.Left(()) => advance() next - case Right(o) => + case Xor.Right(o) => val b = o.get advance() b diff --git a/core/src/main/scala/cats/instances/try.scala b/core/src/main/scala/cats/instances/try.scala index e96d6bb511..7ae2520e7b 100644 --- a/core/src/main/scala/cats/instances/try.scala +++ b/core/src/main/scala/cats/instances/try.scala @@ -1,6 +1,7 @@ package cats package instances +import cats.data.Xor import TryInstances.castFailure import scala.util.control.NonFatal @@ -52,11 +53,11 @@ trait TryInstances extends TryInstances1 { case f: Failure[_] => G.pure(castFailure[B](f)) } - @tailrec final def tailRecM[B, C](b: B)(f: B => Try[Either[B, C]]): Try[C] = + @tailrec final def tailRecM[B, C](b: B)(f: B => Try[(B Xor C)]): Try[C] = f(b) match { case f: Failure[_] => castFailure[C](f) - case Success(Left(b1)) => tailRecM(b1)(f) - case Success(Right(c)) => Success(c) + case Success(Xor.Left(b1)) => tailRecM(b1)(f) + case Success(Xor.Right(c)) => Success(c) } def handleErrorWith[A](ta: Try[A])(f: Throwable => Try[A]): Try[A] = @@ -66,8 +67,8 @@ trait TryInstances extends TryInstances1 { override def handleError[A](ta: Try[A])(f: Throwable => A): Try[A] = ta.recover { case t => f(t) } - override def attempt[A](ta: Try[A]): Try[Either[Throwable, A]] = - (ta.map(a => Right[Throwable, A](a))) recover { case NonFatal(t) => Left(t) } + override def attempt[A](ta: Try[A]): Try[Throwable Xor A] = + (ta map Xor.right) recover { case NonFatal(t) => Xor.left(t) } override def recover[A](ta: Try[A])(pf: PartialFunction[Throwable, A]): Try[A] = ta.recover(pf) diff --git a/core/src/main/scala/cats/instances/vector.scala b/core/src/main/scala/cats/instances/vector.scala index 8126233b32..8f4f2ca48d 100644 --- a/core/src/main/scala/cats/instances/vector.scala +++ b/core/src/main/scala/cats/instances/vector.scala @@ -1,6 +1,8 @@ package cats package instances +import cats.data.Xor + import cats.syntax.show._ import scala.annotation.tailrec import scala.collection.+: @@ -48,7 +50,7 @@ trait VectorInstances extends cats.kernel.instances.VectorInstances { G.map2Eval(f(a), lgvb)((ob, v) => ob.fold(v)(_ +: v)) }.value - def tailRecM[A, B](a: A)(fn: A => Vector[Either[A, B]]): Vector[B] = { + def tailRecM[A, B](a: A)(fn: A => Vector[Xor[A, B]]): Vector[B] = { val buf = Vector.newBuilder[B] var state = List(fn(a).iterator) @tailrec @@ -59,10 +61,10 @@ trait VectorInstances extends cats.kernel.instances.VectorInstances { loop() case h :: tail => h.next match { - case Right(b) => + case Xor.Right(b) => buf += b loop() - case Left(a) => + case Xor.Left(a) => state = (fn(a).iterator) :: h :: tail loop() } diff --git a/core/src/main/scala/cats/package.scala b/core/src/main/scala/cats/package.scala index e0708dd787..b1d72a0fca 100644 --- a/core/src/main/scala/cats/package.scala +++ b/core/src/main/scala/cats/package.scala @@ -1,4 +1,5 @@ import scala.annotation.tailrec +import cats.data.Xor /** * Symbolic aliases for various types are defined here. @@ -33,9 +34,9 @@ package object cats { def extract[A](a: A): A = a def flatMap[A, B](a: A)(f: A => B): B = f(a) def coflatMap[A, B](a: A)(f: A => B): B = f(a) - @tailrec def tailRecM[A, B](a: A)(f: A => Either[A, B]): B = f(a) match { - case Left(a1) => tailRecM(a1)(f) - case Right(b) => b + @tailrec def tailRecM[A, B](a: A)(f: A => A Xor B): B = f(a) match { + case Xor.Left(a1) => tailRecM(a1)(f) + case Xor.Right(b) => b } override def map[A, B](fa: A)(f: A => B): B = f(fa) override def ap[A, B](ff: A => B)(fa: A): B = ff(fa) diff --git a/core/src/main/scala/cats/syntax/applicativeError.scala b/core/src/main/scala/cats/syntax/applicativeError.scala index 10dc9a6079..73c57aef64 100644 --- a/core/src/main/scala/cats/syntax/applicativeError.scala +++ b/core/src/main/scala/cats/syntax/applicativeError.scala @@ -1,7 +1,7 @@ package cats package syntax -import cats.data.EitherT +import cats.data.{Xor, XorT} trait ApplicativeErrorSyntax { implicit def catsSyntaxApplicativeErrorId[E](e: E): ApplicativeErrorIdOps[E] = @@ -24,10 +24,10 @@ final class ApplicativeErrorOps[F[_], E, A](fa: F[A])(implicit F: ApplicativeErr def handleErrorWith(f: E => F[A]): F[A] = F.handleErrorWith(fa)(f) - def attempt: F[Either[E, A]] = + def attempt: F[E Xor A] = F.attempt(fa) - def attemptT: EitherT[F, E, A] = + def attemptT: XorT[F, E, A] = F.attemptT(fa) def recover(pf: PartialFunction[E, A]): F[A] = diff --git a/core/src/main/scala/cats/syntax/either.scala b/core/src/main/scala/cats/syntax/either.scala index f4809f410a..b91efb2635 100644 --- a/core/src/main/scala/cats/syntax/either.scala +++ b/core/src/main/scala/cats/syntax/either.scala @@ -1,232 +1,13 @@ package cats package syntax -import cats.data.{EitherT, Ior, Validated, ValidatedNel, Xor} -import scala.reflect.ClassTag -import scala.util.{Failure, Success, Try} +import cats.data.Xor trait EitherSyntax { implicit def catsSyntaxEither[A, B](eab: Either[A, B]): EitherOps[A, B] = new EitherOps(eab) - - implicit def catsSyntaxEitherObject(either: Either.type): EitherObjectOps = new EitherObjectOps(either) // scalastyle:off ensure.single.space.after.token - - implicit def catsSyntaxLeft[A, B](left: Left[A, B]): LeftOps[A, B] = new LeftOps(left) - - implicit def catsSyntaxRight[A, B](right: Right[A, B]): RightOps[A, B] = new RightOps(right) } final class EitherOps[A, B](val eab: Either[A, B]) extends AnyVal { - def foreach(f: B => Unit): Unit = eab match { - case Left(_) => () - case Right(b) => f(b) - } - - def getOrElse(default: => B): B = eab match { - case Left(_) => default - case Right(b) => b - } - - def orElse[C](fallback: => Either[C, B]): Either[C, B] = eab match { - case Left(_) => fallback - case r @ Right(_) => EitherUtil.leftCast(r) - } - - def recover(pf: PartialFunction[A, B]): Either[A, B] = eab match { - case Left(a) if pf.isDefinedAt(a) => Right(pf(a)) - case _ => eab - } - - def recoverWith(pf: PartialFunction[A, Either[A, B]]): Either[A, B] = eab match { - case Left(a) if pf.isDefinedAt(a) => pf(a) - case _ => eab - } - - def valueOr(f: A => B): B = eab match { - case Left(a) => f(a) - case Right(b) => b - } - - def forall(f: B => Boolean): Boolean = eab match { - case Left(_) => true - case Right(b) => f(b) - } - - def exists(f: B => Boolean): Boolean = eab match { - case Left(_) => false - case Right(b) => f(b) - } - - def ensure(onFailure: => A)(f: B => Boolean): Either[A, B] = eab match { - case Left(_) => eab - case Right(b) => if (f(b)) eab else Left(onFailure) - } - - def toIor: A Ior B = eab match { - case Left(a) => Ior.left(a) - case Right(b) => Ior.right(b) - } - - def toOption: Option[B] = eab match { - case Left(_) => None - case Right(b) => Some(b) - } - - def toList: List[B] = eab match { - case Left(_) => Nil - case Right(b) => List(b) - } - - def toTry(implicit ev: A <:< Throwable): Try[B] = eab match { - case Left(a) => Failure(ev(a)) - case Right(b) => Success(b) - } - - def toValidated: Validated[A, B] = eab match { - case Left(a) => Validated.invalid(a) - case Right(b) => Validated.valid(b) - } - - /** Returns a [[cats.data.ValidatedNel]] representation of this disjunction with the `Left` value - * as a single element on the `Invalid` side of the [[cats.data.NonEmptyList]]. */ - def toValidatedNel: ValidatedNel[A, B] = eab match { - case Left(a) => Validated.invalidNel(a) - case Right(b) => Validated.valid(b) - } - - def withValidated[AA, BB](f: Validated[A, B] => Validated[AA, BB]): Either[AA, BB] = - f(toValidated).toEither - - def to[F[_]](implicit F: Alternative[F]): F[B] = eab match { - case Left(_) => F.empty - case Right(b) => F.pure(b) - } - - def bimap[C, D](fa: A => C, fb: B => D): Either[C, D] = eab match { - case Left(a) => Left(fa(a)) - case Right(b) => Right(fb(b)) - } - - def map[C](f: B => C): Either[A, C] = eab match { - case l @ Left(_) => EitherUtil.rightCast(l) - case Right(b) => Right(f(b)) - } - - def map2Eval[C, Z](fc: Eval[Either[A, C]])(f: (B, C) => Z): Eval[Either[A, Z]] = - eab match { - case l @ Left(_) => Now(EitherUtil.rightCast(l)) - case Right(b) => fc.map(either => new EitherOps(either).map(f(b, _))) - } - - def leftMap[C](f: A => C): Either[C, B] = eab match { - case Left(a) => Left(f(a)) - case r @ Right(_) => EitherUtil.leftCast(r) - } - - def flatMap[D](f: B => Either[A, D]): Either[A, D] = eab match { - case l @ Left(_) => EitherUtil.rightCast(l) - case Right(b) => f(b) - } - - def compare(that: Either[A, B])(implicit A: Order[A], B: Order[B]): Int = eab match { - case Left(a1) => - that match { - case Left(a2) => A.compare(a1, a2) - case Right(_) => -1 - } - case Right(b1) => - that match { - case Left(_) => 1 - case Right(b2) => B.compare(b1, b2) - } - } - - def partialCompare(that: Either[A, B])(implicit A: PartialOrder[A], B: PartialOrder[B]): Double = eab match { - case Left(a1) => - that match { - case Left(a2) => A.partialCompare(a1, a2) - case Right(_) => -1 - } - case Right(b1) => - that match { - case Left(_) => 1 - case Right(b2) => B.partialCompare(b1, b2) - } - } - - def ===(that: Either[A, B])(implicit A: Eq[A], B: Eq[B]): Boolean = eab match { - case Left(a1) => - that match { - case Left(a2) => A.eqv(a1, a2) - case Right(_) => false - } - case Right(b1) => - that match { - case Left(_) => false - case Right(b2) => B.eqv(b1, b2) - } - } - - def traverse[F[_], D](f: B => F[D])(implicit F: Applicative[F]): F[Either[A, D]] = eab match { - case l @ Left(_) => F.pure(EitherUtil.rightCast(l)) - case Right(b) => F.map(f(b))(Right(_)) - } - - def foldLeft[C](c: C)(f: (C, B) => C): C = eab match { - case Left(_) => c - case Right(b) => f(c, b) - } - - def foldRight[C](lc: Eval[C])(f: (B, Eval[C]) => Eval[C]): Eval[C] = eab match { - case Left(_) => lc - case Right(b) => f(b, lc) - } - - /** - * Combine with another `Either` value. - * - * If this `Either` is a `Left` then it will be returned as-is. - * If this `Either` is a `Right` and `that` `Either` is a left, then `that` will be - * returned. - * If both `Either`s are `Right`s, then the `Semigroup[BB]` instance will be used - * to combine both values and return them as a `Right`. - * Note: If both `Either`s are `Left`s then their values are not combined. Use - * `Validated` if you prefer to combine `Left` values. - * - * Examples: - * {{{ - * scala> import cats.implicits._ - * scala> val l1: Either[String, Int] = Either.left("error 1") - * scala> val l2: Either[String, Int] = Either.left("error 2") - * scala> val r3: Either[String, Int] = Either.right(3) - * scala> val r4: Either[String, Int] = Either.right(4) - * - * scala> l1 combine l2 - * res0: Either[String, Int] = Left(error 1) - * - * scala> l1 combine r3 - * res1: Either[String, Int] = Left(error 1) - * - * scala> r3 combine l1 - * res2: Either[String, Int] = Left(error 1) - * - * scala> r3 combine r4 - * res3: Either[String, Int] = Right(7) - * }}} - */ - final def combine(that: Either[A, B])(implicit B: Semigroup[B]): Either[A, B] = eab match { - case left @ Left(_) => left - case Right(b1) => that match { - case left @ Left(_) => left - case Right(b2) => Right(B.combine(b1, b2)) - } - } - - def show(implicit A: Show[A], B: Show[B]): String = eab match { - case Left(a) => s"Left(${A.show(a)})" - case Right(b) => s"Right(${B.show(b)})" - } - - def ap[C](that: Either[A, B => C]): Either[A, C] = (new EitherOps(that)).flatMap(this.map) /** * Convert a `scala.util.Either` into a [[cats.data.Xor]]. @@ -246,88 +27,4 @@ final class EitherOps[A, B](val eab: Either[A, B]) extends AnyVal { * }}} */ def toXor: A Xor B = Xor.fromEither(eab) - - /** - * Transform the `Either` into a [[cats.data.EitherT]] while lifting it into the specified Applicative. - * - * {{{ - * scala> import cats.implicits._ - * scala> val e: Either[String, Int] = Right(3) - * scala> e.toEitherT[Option] - * res0: cats.data.EitherT[Option, String, Int] = EitherT(Some(Right(3))) - * }}} - */ - def toEitherT[F[_]: Applicative]: EitherT[F, A, B] = EitherT.fromEither(eab) -} - -final class EitherObjectOps(val either: Either.type) extends AnyVal { // scalastyle:off ensure.single.space.after.token - def left[A, B](a: A): Either[A, B] = Left(a) - - def right[A, B](b: B): Either[A, B] = Right(b) - - /** - * Evaluates the specified block, catching exceptions of the specified type and returning them on the left side of - * the resulting `Either`. Uncaught exceptions are propagated. - * - * For example: - * {{{ - * scala> import cats.implicits._ // get syntax for Either - * scala> Either.catchOnly[NumberFormatException] { "foo".toInt } - * res0: Either[NumberFormatException, Int] = Left(java.lang.NumberFormatException: For input string: "foo") - * }}} - */ - def catchOnly[T >: Null <: Throwable]: CatchOnlyPartiallyApplied[T] = - new CatchOnlyPartiallyApplied[T] - - def catchNonFatal[A](f: => A): Either[Throwable, A] = - try { - right(f) - } catch { - case scala.util.control.NonFatal(t) => left(t) - } - - /** - * Converts a `Try[A]` to a `Throwable Xor A`. - */ - def fromTry[A](t: Try[A]): Either[Throwable, A] = - t match { - case Failure(e) => left(e) - case Success(v) => right(v) - } - - /** - * Converts an `Option[B]` to an `A Xor B`, where the provided `ifNone` values is returned on - * the left of the `Xor` when the specified `Option` is `None`. - */ - def fromOption[A, B](o: Option[B], ifNone: => A): Either[A, B] = o match { - case None => left[A, B](ifNone) - case Some(a) => right(a) - } -} - -final class CatchOnlyPartiallyApplied[T] private[syntax] { - def apply[A](f: => A)(implicit CT: ClassTag[T], NT: NotNull[T]): Either[T, A] = - try { - Right(f) - } catch { - case t if CT.runtimeClass.isInstance(t) => - Left(t.asInstanceOf[T]) - } -} - -final class LeftOps[A, B](val left: Left[A, B]) extends AnyVal { - /** Cast the right type parameter of the `Left`. */ - def rightCast[C]: Either[A, C] = left.asInstanceOf[Either[A, C]] -} - -final class RightOps[A, B](val right: Right[A, B]) extends AnyVal { - /** Cast the left type parameter of the `Right`. */ - def leftCast[C]: Either[C, B] = right.asInstanceOf[Either[C, B]] -} - -/** Convenience methods to use `Either` syntax inside `Either` syntax definitions. */ -private[cats] object EitherUtil { - def leftCast[A, B, C](r: Right[A, B]): Either[C, B] = new RightOps(r).leftCast[C] - - def rightCast[A, B, C](l: Left[A, B]): Either[A, C] = new LeftOps(l).rightCast[C] } diff --git a/core/src/main/scala/cats/syntax/flatMap.scala b/core/src/main/scala/cats/syntax/flatMap.scala index 1723dddf83..057ce263f4 100644 --- a/core/src/main/scala/cats/syntax/flatMap.scala +++ b/core/src/main/scala/cats/syntax/flatMap.scala @@ -25,9 +25,10 @@ final class FlattenOps[F[_], A](ffa: F[F[A]])(implicit F: FlatMap[F]) { * * Example: * {{{ + * scala> import cats.data.Xor * scala> import cats.implicits._ - * scala> type ErrorOr[A] = Either[String, A] - * scala> val x: ErrorOr[ErrorOr[Int]] = Right(Right(3)) + * scala> type ErrorOr[A] = String Xor A + * scala> val x: ErrorOr[ErrorOr[Int]] = Xor.right(Xor.right(3)) * scala> x.flatten * res0: ErrorOr[Int] = Right(3) * }}} diff --git a/core/src/main/scala/cats/syntax/monadCombine.scala b/core/src/main/scala/cats/syntax/monadCombine.scala index 40e53f2449..53cbbd4fdb 100644 --- a/core/src/main/scala/cats/syntax/monadCombine.scala +++ b/core/src/main/scala/cats/syntax/monadCombine.scala @@ -33,8 +33,9 @@ final class SeparateOps[F[_], G[_, _], A, B](fgab: F[G[A, B]])(implicit F: Monad * * Example: * {{{ + * scala> import cats.data.Xor * scala> import cats.implicits._ - * scala> val l: List[Either[String, Int]] = List(Right(1), Left("error")) + * scala> val l: List[Xor[String, Int]] = List(Xor.right(1), Xor.left("error")) * scala> l.separate * res0: (List[String], List[Int]) = (List(error),List(1)) * }}} diff --git a/docs/src/main/tut/kleisli.md b/docs/src/main/tut/kleisli.md index f446cb8015..39a4768cda 100644 --- a/docs/src/main/tut/kleisli.md +++ b/docs/src/main/tut/kleisli.md @@ -138,7 +138,7 @@ implicit def kleisliFlatMap[F[_], Z](implicit F: FlatMap[F]): FlatMap[Kleisli[F, def map[A, B](fa: Kleisli[F, Z, A])(f: A => B): Kleisli[F, Z, B] = Kleisli(z => fa.run(z).map(f)) - def tailRecM[A, B](a: A)(f: A => Kleisli[F, Z, Either[A, B]]) = + def tailRecM[A, B](a: A)(f: A => Kleisli[F, Z, Xor[A, B]]) = Kleisli[F, Z, B]({ z => FlatMap[F].tailRecM(a) { f(_).run(z) } }) } ``` diff --git a/docs/src/main/tut/monad.md b/docs/src/main/tut/monad.md index 54626636d9..a4973f3f94 100644 --- a/docs/src/main/tut/monad.md +++ b/docs/src/main/tut/monad.md @@ -35,6 +35,7 @@ the identity function `x => x` (i.e. `flatMap(_)(x => x)`). ```tut:silent import cats._ +import cats.data.Xor implicit def optionMonad(implicit app: Applicative[Option]) = new Monad[Option] { @@ -45,11 +46,11 @@ implicit def optionMonad(implicit app: Applicative[Option]) = override def pure[A](a: A): Option[A] = app.pure(a) @annotation.tailrec - def tailRecM[A, B](init: A)(fn: A => Option[Either[A, B]]): Option[B] = + def tailRecM[A, B](init: A)(fn: A => Option[Xor[A, B]]): Option[B] = fn(init) match { case None => None - case Some(Right(b)) => Some(b) - case Some(Left(a)) => tailRecM(a)(fn) + case Some(Xor.Right(b)) => Some(b) + case Some(Xor.Left(a)) => tailRecM(a)(fn) } } ``` @@ -64,7 +65,7 @@ derived from `flatMap` and `pure`. implicit val listMonad = new Monad[List] { def flatMap[A, B](fa: List[A])(f: A => List[B]): List[B] = fa.flatMap(f) def pure[A](a: A): List[A] = List(a) - def tailRecM[A, B](a: A)(f: A => List[Either[A, B]]): List[B] = + def tailRecM[A, B](a: A)(f: A => List[Xor[A, B]]): List[B] = defaultTailRecM(a)(f) } ``` @@ -119,7 +120,7 @@ implicit def optionTMonad[F[_]](implicit F : Monad[F]) = { case Some(a) => f(a).value } } - def tailRecM[A, B](a: A)(f: A => OptionT[F, Either[A, B]]): OptionT[F, B] = + def tailRecM[A, B](a: A)(f: A => OptionT[F, Xor[A, B]]): OptionT[F, B] = defaultTailRecM(a)(f) } } diff --git a/docs/src/main/tut/validated.md b/docs/src/main/tut/validated.md index d398e60829..b9ac49da13 100644 --- a/docs/src/main/tut/validated.md +++ b/docs/src/main/tut/validated.md @@ -253,10 +253,10 @@ implicit def validatedMonad[E]: Monad[Validated[E, ?]] = def pure[A](x: A): Validated[E, A] = Valid(x) @annotation.tailrec - def tailRecM[A, B](a: A)(f: A => Validated[E, Either[A, B]]): Validated[E, B] = + def tailRecM[A, B](a: A)(f: A => Validated[E, Xor[A, B]]): Validated[E, B] = f(a) match { - case Valid(Right(b)) => Valid(b) - case Valid(Left(a)) => tailRecM(a)(f) + case Valid(Xor.Right(b)) => Valid(b) + case Valid(Xor.Left(a)) => tailRecM(a)(f) case i@Invalid(_) => i } } @@ -314,19 +314,19 @@ val houseNumber = config.parse[Int]("house_number").andThen{ n => The `withXor` method allows you to temporarily turn a `Validated` instance into an `Xor` instance and apply it to a function. ```tut:silent -import cats.syntax.either._ // get Either#flatMap +import cats.data.Xor -def positive(field: String, i: Int): Either[ConfigError, Int] = { - if (i >= 0) Right(i) - else Left(ParseError(field)) +def positive(field: String, i: Int): ConfigError Xor Int = { + if (i >= 0) Xor.right(i) + else Xor.left(ParseError(field)) } ``` Thus. ```tut:book -val houseNumber = config.parse[Int]("house_number").withEither{ either: Either[ConfigError, Int] => - either.flatMap{ i => +val houseNumber = config.parse[Int]("house_number").withXor{ xor: ConfigError Xor Int => + xor.flatMap{ i => positive("house_number", i) } } diff --git a/docs/src/main/tut/xor.md b/docs/src/main/tut/xor.md index ad14832991..118b40c782 100644 --- a/docs/src/main/tut/xor.md +++ b/docs/src/main/tut/xor.md @@ -113,10 +113,10 @@ implicit def xorMonad[Err]: Monad[Xor[Err, ?]] = def pure[A](x: A): Xor[Err, A] = Xor.right(x) @annotation.tailrec - def tailRecM[A, B](a: A)(f: A => Xor[Err, Either[A, B]]): Xor[Err, B] = + def tailRecM[A, B](a: A)(f: A => Xor[Err, Xor[A, B]]): Xor[Err, B] = f(a) match { - case Xor.Right(Right(b)) => Xor.right(b) - case Xor.Right(Left(a)) => tailRecM(a)(f) + case Xor.Right(Xor.Right(b)) => Xor.right(b) + case Xor.Right(Xor.Left(a)) => tailRecM(a)(f) case l@Xor.Left(_) => l } } diff --git a/free/src/main/scala/cats/free/Free.scala b/free/src/main/scala/cats/free/Free.scala index 0ccbca977e..feb9ab9098 100644 --- a/free/src/main/scala/cats/free/Free.scala +++ b/free/src/main/scala/cats/free/Free.scala @@ -3,6 +3,7 @@ package free import scala.annotation.tailrec +import cats.data.Xor, Xor.{Left, Right} import cats.arrow.FunctionK /** @@ -43,7 +44,7 @@ sealed abstract class Free[S[_], A] extends Product with Serializable { * Evaluate a single layer of the free monad. */ @tailrec - final def resume(implicit S: Functor[S]): Either[S[Free[S, A]], A] = this match { + final def resume(implicit S: Functor[S]): S[Free[S, A]] Xor A = this match { case Pure(a) => Right(a) case Suspend(t) => Left(S.map(t)(Pure(_))) case FlatMapped(c, f) => @@ -91,7 +92,7 @@ sealed abstract class Free[S[_], A] extends Product with Serializable { * resumption in the context of `S`. */ final def runTailRec(implicit S: Monad[S], r: RecursiveTailRecM[S]): S[A] = { - def step(rma: Free[S, A]): S[Either[Free[S, A], A]] = + def step(rma: Free[S, A]): S[Xor[Free[S, A], A]] = rma match { case Pure(a) => S.pure(Right(a)) @@ -213,7 +214,7 @@ object Free { def pure[A](a: A): Free[S, A] = Free.pure(a) override def map[A, B](fa: Free[S, A])(f: A => B): Free[S, B] = fa.map(f) def flatMap[A, B](a: Free[S, A])(f: A => Free[S, B]): Free[S, B] = a.flatMap(f) - def tailRecM[A, B](a: A)(f: A => Free[S, Either[A, B]]): Free[S, B] = + def tailRecM[A, B](a: A)(f: A => Free[S, A Xor B]): Free[S, B] = f(a).flatMap(_ match { case Left(a1) => tailRecM(a1)(f) // recursion OK here, since Free is lazy case Right(b) => pure(b) diff --git a/free/src/test/scala/cats/free/FreeTests.scala b/free/src/test/scala/cats/free/FreeTests.scala index 676a425c5f..bdcaa7c7b2 100644 --- a/free/src/test/scala/cats/free/FreeTests.scala +++ b/free/src/test/scala/cats/free/FreeTests.scala @@ -3,6 +3,7 @@ package free import cats.tests.CatsSuite import cats.arrow.FunctionK +import cats.data.Xor import cats.laws.discipline.{CartesianTests, MonadTests, SerializableTests} import cats.laws.discipline.arbitrary.catsLawsArbitraryForFn0 @@ -52,7 +53,7 @@ class FreeTests extends CatsSuite { test("tailRecM is stack safe") { val n = 50000 val fa = Monad[Free[Option, ?]].tailRecM(0)(i => - Free.pure[Option, Either[Int, Int]](if (i < n) Left(i+1) else Right(i))) + Free.pure[Option, Xor[Int, Int]](if (i < n) Xor.Left(i+1) else Xor.Right(i))) fa should === (Free.pure[Option, Int](n)) } @@ -91,18 +92,18 @@ class FreeTests extends CatsSuite { // changing the constant argument to .take and observing the time // this test takes. val ns = Stream.from(1).take(1000) - val res = Free.foldLeftM[Stream, Either[Int, ?], Int, Int](ns, 0) { (sum, n) => - if (sum >= 2) Either.left(sum) else Either.right(sum + n) + val res = Free.foldLeftM[Stream, Xor[Int, ?], Int, Int](ns, 0) { (sum, n) => + if (sum >= 2) Xor.left(sum) else Xor.right(sum + n) } - assert(res == Either.left(3)) + assert(res == Xor.left(3)) } test(".foldLeftM short-circuiting") { val ns = Stream.continually(1) - val res = Free.foldLeftM[Stream, Either[Int, ?], Int, Int](ns, 0) { (sum, n) => - if (sum >= 100000) Either.left(sum) else Either.right(sum + n) + val res = Free.foldLeftM[Stream, Xor[Int, ?], Int, Int](ns, 0) { (sum, n) => + if (sum >= 100000) Xor.left(sum) else Xor.right(sum + n) } - assert(res == Either.left(100000)) + assert(res == Xor.left(100000)) } } diff --git a/free/src/test/scala/cats/free/InjectTests.scala b/free/src/test/scala/cats/free/InjectTests.scala index 8e1a97dcaf..ae922e63ba 100644 --- a/free/src/test/scala/cats/free/InjectTests.scala +++ b/free/src/test/scala/cats/free/InjectTests.scala @@ -3,7 +3,7 @@ package free import cats.arrow.FunctionK import cats.tests.CatsSuite -import cats.data.Coproduct +import cats.data.{Xor, Coproduct} import org.scalacheck._ class InjectTests extends CatsSuite { @@ -91,13 +91,13 @@ class InjectTests extends CatsSuite { test("apply in left") { forAll { (y: Test1[Int]) => - Inject[Test1Algebra, T].inj(y) == Coproduct(Left(y)) should ===(true) + Inject[Test1Algebra, T].inj(y) == Coproduct(Xor.Left(y)) should ===(true) } } test("apply in right") { forAll { (y: Test2[Int]) => - Inject[Test2Algebra, T].inj(y) == Coproduct(Right(y)) should ===(true) + Inject[Test2Algebra, T].inj(y) == Coproduct(Xor.Right(y)) should ===(true) } } diff --git a/js/src/test/scala/cats/tests/FutureTests.scala b/js/src/test/scala/cats/tests/FutureTests.scala index ec982d8817..17d69da4fd 100644 --- a/js/src/test/scala/cats/tests/FutureTests.scala +++ b/js/src/test/scala/cats/tests/FutureTests.scala @@ -2,6 +2,7 @@ package cats package js package tests +import cats.data.Xor import cats.laws.discipline._ import cats.js.instances.Await import cats.js.instances.future.futureComonad @@ -24,13 +25,13 @@ import DeprecatedForwarder.runNow class FutureTests extends CatsSuite { val timeout = 3.seconds - def futureEither[A](f: Future[A]): Future[Either[Throwable, A]] = - f.map(Either.right[Throwable, A]).recover { case t => Either.left(t) } + def futureXor[A](f: Future[A]): Future[Xor[Throwable, A]] = + f.map(Xor.right[Throwable, A]).recover { case t => Xor.left(t) } implicit def eqfa[A: Eq]: Eq[Future[A]] = new Eq[Future[A]] { def eqv(fx: Future[A], fy: Future[A]): Boolean = { - val fz = futureEither(fx) zip futureEither(fy) + val fz = futureXor(fx) zip futureXor(fy) Await.result(fz.map { case (tx, ty) => tx === ty }, timeout) } } diff --git a/jvm/src/test/scala/cats/tests/FutureTests.scala b/jvm/src/test/scala/cats/tests/FutureTests.scala index af68328617..0c283bebb9 100644 --- a/jvm/src/test/scala/cats/tests/FutureTests.scala +++ b/jvm/src/test/scala/cats/tests/FutureTests.scala @@ -2,6 +2,7 @@ package cats package jvm package tests +import cats.data.Xor import cats.laws.discipline._ import cats.tests.CatsSuite @@ -15,13 +16,13 @@ import org.scalacheck.Arbitrary.arbitrary class FutureTests extends CatsSuite { val timeout = 3.seconds - def futureEither[A](f: Future[A]): Future[Either[Throwable, A]] = - f.map(Either.right[Throwable, A]).recover { case t => Either.left(t) } + def futureXor[A](f: Future[A]): Future[Xor[Throwable, A]] = + f.map(Xor.right[Throwable, A]).recover { case t => Xor.left(t) } implicit def eqfa[A: Eq]: Eq[Future[A]] = new Eq[Future[A]] { def eqv(fx: Future[A], fy: Future[A]): Boolean = { - val fz = futureEither(fx) zip futureEither(fy) + val fz = futureXor(fx) zip futureXor(fy) Await.result(fz.map { case (tx, ty) => tx === ty }, timeout) } } diff --git a/laws/src/main/scala/cats/laws/ApplicativeErrorLaws.scala b/laws/src/main/scala/cats/laws/ApplicativeErrorLaws.scala index 6dd12d4e21..b912534bf7 100644 --- a/laws/src/main/scala/cats/laws/ApplicativeErrorLaws.scala +++ b/laws/src/main/scala/cats/laws/ApplicativeErrorLaws.scala @@ -1,7 +1,7 @@ package cats package laws -import cats.data.EitherT +import cats.data.{Xor, XorT} // Taken from http://functorial.com/psc-pages/docs/Control/Monad/Error/Class/index.html trait ApplicativeErrorLaws[F[_], E] extends ApplicativeLaws[F] { @@ -19,11 +19,11 @@ trait ApplicativeErrorLaws[F[_], E] extends ApplicativeLaws[F] { def handleErrorPure[A](a: A, f: E => A): IsEq[F[A]] = F.handleError(F.pure(a))(f) <-> F.pure(a) - def raiseErrorAttempt(e: E): IsEq[F[Either[E, Unit]]] = - F.attempt(F.raiseError[Unit](e)) <-> F.pure(Left(e)) + def raiseErrorAttempt(e: E): IsEq[F[E Xor Unit]] = + F.attempt(F.raiseError[Unit](e)) <-> F.pure(Xor.left(e)) - def pureAttempt[A](a: A): IsEq[F[Either[E, A]]] = - F.attempt(F.pure(a)) <-> F.pure(Right(a)) + def pureAttempt[A](a: A): IsEq[F[E Xor A]] = + F.attempt(F.pure(a)) <-> F.pure(Xor.right(a)) def handleErrorWithConsistentWithRecoverWith[A](fa: F[A], f: E => F[A]): IsEq[F[A]] = F.handleErrorWith(fa)(f) <-> F.recoverWith(fa)(PartialFunction(f)) @@ -34,8 +34,8 @@ trait ApplicativeErrorLaws[F[_], E] extends ApplicativeLaws[F] { def recoverConsistentWithRecoverWith[A](fa: F[A], pf: PartialFunction[E, A]): IsEq[F[A]] = F.recover(fa)(pf) <-> F.recoverWith(fa)(pf andThen F.pure) - def attemptConsistentWithAttemptT[A](fa: F[A]): IsEq[EitherT[F, E, A]] = - EitherT(F.attempt(fa)) <-> F.attemptT(fa) + def attemptConsistentWithAttemptT[A](fa: F[A]): IsEq[XorT[F, E, A]] = + XorT(F.attempt(fa)) <-> F.attemptT(fa) } object ApplicativeErrorLaws { diff --git a/laws/src/main/scala/cats/laws/ChoiceLaws.scala b/laws/src/main/scala/cats/laws/ChoiceLaws.scala index f18f520e20..71428186f2 100644 --- a/laws/src/main/scala/cats/laws/ChoiceLaws.scala +++ b/laws/src/main/scala/cats/laws/ChoiceLaws.scala @@ -2,6 +2,7 @@ package cats package laws import cats.arrow.Choice +import cats.data.Xor import cats.syntax.compose._ /** @@ -10,7 +11,7 @@ import cats.syntax.compose._ trait ChoiceLaws[F[_, _]] extends CategoryLaws[F] { implicit override def F: Choice[F] - def choiceCompositionDistributivity[A, B, C, D](fac: F[A, C], fbc: F[B, C], fcd: F[C, D]): IsEq[F[Either[A, B], D]] = + def choiceCompositionDistributivity[A, B, C, D](fac: F[A, C], fbc: F[B, C], fcd: F[C, D]): IsEq[F[Xor[A, B], D]] = (F.choice(fac, fbc) andThen fcd) <-> F.choice(fac andThen fcd, fbc andThen fcd) } diff --git a/laws/src/main/scala/cats/laws/FlatMapLaws.scala b/laws/src/main/scala/cats/laws/FlatMapLaws.scala index 5b87c86646..088ba748ce 100644 --- a/laws/src/main/scala/cats/laws/FlatMapLaws.scala +++ b/laws/src/main/scala/cats/laws/FlatMapLaws.scala @@ -1,7 +1,7 @@ package cats package laws -import cats.data.Kleisli +import cats.data.{Kleisli, Xor} import cats.syntax.apply._ import cats.syntax.flatMap._ import cats.syntax.functor._ @@ -29,8 +29,8 @@ trait FlatMapLaws[F[_]] extends ApplyLaws[F] { def tailRecMConsistentFlatMap[A](count: Int, a: A, f: A => F[A]): IsEq[F[A]] = { def bounce(n: Int) = F.tailRecM[(A, Int), A]((a, n)) { case (a0, i) => - if (i > 0) f(a0).map(a1 => Left((a1, i-1))) - else f(a0).map(Right(_)) + if (i > 0) f(a0).map(a1 => Xor.Left((a1, i-1))) + else f(a0).map(Xor.Right(_)) } /* * The law is for n >= 1 diff --git a/laws/src/main/scala/cats/laws/discipline/ApplicativeErrorTests.scala b/laws/src/main/scala/cats/laws/discipline/ApplicativeErrorTests.scala index 8116da9f03..036e2a8469 100644 --- a/laws/src/main/scala/cats/laws/discipline/ApplicativeErrorTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/ApplicativeErrorTests.scala @@ -2,7 +2,7 @@ package cats package laws package discipline -import cats.data.EitherT +import cats.data.{ Xor, XorT } import cats.laws.discipline.CartesianTests.Isomorphisms import cats.laws.discipline.arbitrary._ import org.scalacheck.{Arbitrary, Prop} @@ -22,9 +22,9 @@ trait ApplicativeErrorTests[F[_], E] extends ApplicativeTests[F] { EqFB: Eq[F[B]], EqFC: Eq[F[C]], EqE: Eq[E], - EqFEitherEU: Eq[F[Either[E, Unit]]], - EqFEitherEA: Eq[F[Either[E, A]]], - EqEitherTFEA: Eq[EitherT[F, E, A]], + EqFXorEU: Eq[F[E Xor Unit]], + EqFXorEA: Eq[F[E Xor A]], + EqXorTFEA: Eq[XorT[F, E, A]], EqFABC: Eq[F[(A, B, C)]], iso: Isomorphisms[F] ): RuleSet = { diff --git a/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala b/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala index 4e9801c6e7..344ce42f1b 100644 --- a/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala +++ b/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala @@ -41,9 +41,6 @@ object arbitrary extends ArbitraryInstances0 { implicit def catsLawsArbitraryForXorT[F[_], A, B](implicit F: Arbitrary[F[A Xor B]]): Arbitrary[XorT[F, A, B]] = Arbitrary(F.arbitrary.map(XorT(_))) - implicit def catsLawsArbitraryForEitherT[F[_], A, B](implicit F: Arbitrary[F[Either[A, B]]]): Arbitrary[EitherT[F, A, B]] = - Arbitrary(F.arbitrary.map(EitherT(_))) - implicit def catsLawsArbitraryForValidated[A, B](implicit A: Arbitrary[A], B: Arbitrary[B]): Arbitrary[Validated[A, B]] = Arbitrary(Gen.oneOf(A.arbitrary.map(Validated.invalid), B.arbitrary.map(Validated.valid))) diff --git a/laws/src/main/scala/cats/laws/discipline/ChoiceTests.scala b/laws/src/main/scala/cats/laws/discipline/ChoiceTests.scala index 0244679d67..8b2e42f1b1 100644 --- a/laws/src/main/scala/cats/laws/discipline/ChoiceTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/ChoiceTests.scala @@ -3,6 +3,7 @@ package laws package discipline import cats.arrow.Choice +import cats.data.Xor import org.scalacheck.Arbitrary import org.scalacheck.Prop._ @@ -16,7 +17,7 @@ trait ChoiceTests[F[_, _]] extends CategoryTests[F] { ArbFCD: Arbitrary[F[C, D]], EqFAB: Eq[F[A, B]], EqFAD: Eq[F[A, D]], - EqFEitherABD: Eq[F[Either[A, B], D]] + EqFXorABD: Eq[F[Xor[A, B], D]] ): RuleSet = new DefaultRuleSet( name = "choice", diff --git a/laws/src/main/scala/cats/laws/discipline/MonadErrorTests.scala b/laws/src/main/scala/cats/laws/discipline/MonadErrorTests.scala index 74bca14988..732159043b 100644 --- a/laws/src/main/scala/cats/laws/discipline/MonadErrorTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/MonadErrorTests.scala @@ -2,7 +2,7 @@ package cats package laws package discipline -import cats.data.EitherT +import cats.data.{ Xor, XorT } import cats.laws.discipline.CartesianTests.Isomorphisms import org.scalacheck.{Arbitrary, Prop} import org.scalacheck.Prop.forAll @@ -21,9 +21,9 @@ trait MonadErrorTests[F[_], E] extends ApplicativeErrorTests[F, E] with MonadTes EqFB: Eq[F[B]], EqFC: Eq[F[C]], EqE: Eq[E], - EqFEitherEU: Eq[F[Either[E, Unit]]], - EqFEitherEA: Eq[F[Either[E, A]]], - EqEitherTFEA: Eq[EitherT[F, E, A]], + EqFXorEU: Eq[F[E Xor Unit]], + EqFXorEA: Eq[F[E Xor A]], + EqXorTFEA: Eq[XorT[F, E, A]], EqFABC: Eq[F[(A, B, C)]], iso: Isomorphisms[F] ): RuleSet = { diff --git a/tests/src/test/scala/cats/tests/ApplicativeErrorTests.scala b/tests/src/test/scala/cats/tests/ApplicativeErrorTests.scala index 7b8d8ab006..78b71ac964 100644 --- a/tests/src/test/scala/cats/tests/ApplicativeErrorTests.scala +++ b/tests/src/test/scala/cats/tests/ApplicativeErrorTests.scala @@ -1,9 +1,13 @@ package cats package tests -import cats.data.EitherT +import cats.data.{XorT} + class ApplicativeErrorCheck extends CatsSuite { + + + val failed: Option[Int] = (()).raiseError[Option, Int] @@ -19,12 +23,12 @@ class ApplicativeErrorCheck extends CatsSuite { failed.handleErrorWith(_ => Some(7)) should === (Some(7)) } - test("attempt syntax creates a wrapped Either") { - failed.attempt should === (Option(Left(()))) + test("attempt syntax creates a wrapped Xor") { + failed.attempt should === (Option(().left)) } - test("attemptT syntax creates an EitherT") { - failed.attemptT should === (EitherT[Option, Unit, Int](Option(Left(())))) + test("attemptT syntax creates an XorT") { + failed.attemptT should === (XorT[Option, Unit, Int](Option(().left))) } test("recover syntax transforms an error to a success") { diff --git a/tests/src/test/scala/cats/tests/BifoldableTests.scala b/tests/src/test/scala/cats/tests/BifoldableTests.scala index 8dc75de41c..2c121909bd 100644 --- a/tests/src/test/scala/cats/tests/BifoldableTests.scala +++ b/tests/src/test/scala/cats/tests/BifoldableTests.scala @@ -1,13 +1,15 @@ package cats package tests +import cats.data.Xor import cats.laws.discipline.{BifoldableTests, SerializableTests} +import cats.laws.discipline.arbitrary._ class BifoldableTest extends CatsSuite { - type EitherEither[A, B] = Either[Either[A, B], Either[A, B]] - val eitherComposeEither: Bifoldable[EitherEither] = - Bifoldable[Either].compose[Either] + type EitherXor[A, B] = Either[Xor[A, B], Xor[A, B]] + val eitherComposeXor: Bifoldable[EitherXor] = + Bifoldable[Either].compose[Xor] - checkAll("Either compose Either", BifoldableTests(eitherComposeEither).bifoldable[Int, Int, Int]) - checkAll("Bifoldable[Either compose Either]", SerializableTests.serializable(eitherComposeEither)) + checkAll("Either compose Xor", BifoldableTests(eitherComposeXor).bifoldable[Int, Int, Int]) + checkAll("Bifoldable[Either compose Xor]", SerializableTests.serializable(eitherComposeXor)) } diff --git a/tests/src/test/scala/cats/tests/BitraverseTests.scala b/tests/src/test/scala/cats/tests/BitraverseTests.scala index f438b36b19..989c21822a 100644 --- a/tests/src/test/scala/cats/tests/BitraverseTests.scala +++ b/tests/src/test/scala/cats/tests/BitraverseTests.scala @@ -1,13 +1,15 @@ package cats package tests +import cats.data.Xor import cats.laws.discipline.{BitraverseTests, SerializableTests} +import cats.laws.discipline.arbitrary._ class BitraverseTest extends CatsSuite { - type EitherTuple2[A, B] = Either[(A, B), (A, B)] - val eitherComposeTuple2: Bitraverse[EitherTuple2] = - Bitraverse[Either].compose[Tuple2] + type XorTuple2[A, B] = Xor[(A, B), (A, B)] + val xorComposeTuple2: Bitraverse[XorTuple2] = + Bitraverse[Xor].compose[Tuple2] - checkAll("Either compose Tuple2", BitraverseTests(eitherComposeTuple2).bitraverse[Option, Int, Int, Int, String, String, String]) - checkAll("Bitraverse[Either compose Tuple2]", SerializableTests.serializable(eitherComposeTuple2)) + checkAll("Xor compose Tuple2", BitraverseTests(xorComposeTuple2).bitraverse[Option, Int, Int, Int, String, String, String]) + checkAll("Bitraverse[Xor compose Tuple2]", SerializableTests.serializable(xorComposeTuple2)) } diff --git a/tests/src/test/scala/cats/tests/CoproductTests.scala b/tests/src/test/scala/cats/tests/CoproductTests.scala index 059c687343..dc2e4c4d55 100644 --- a/tests/src/test/scala/cats/tests/CoproductTests.scala +++ b/tests/src/test/scala/cats/tests/CoproductTests.scala @@ -53,9 +53,9 @@ class CoproductTests extends CatsSuite { } } - test("toValidated + toEither is identity") { + test("toValidated + toXor is identity") { forAll { (x: Coproduct[Option, List, Int]) => - x.toValidated.toEither should === (x.run) + x.toValidated.toXor should === (x.run) } } } diff --git a/tests/src/test/scala/cats/tests/EitherTTests.scala b/tests/src/test/scala/cats/tests/EitherTTests.scala deleted file mode 100644 index 94ba2b4122..0000000000 --- a/tests/src/test/scala/cats/tests/EitherTTests.scala +++ /dev/null @@ -1,344 +0,0 @@ -package cats -package tests - -import cats.data.EitherT -import cats.functor.Bifunctor -import cats.laws.discipline._ -import cats.laws.discipline.arbitrary._ -import cats.kernel.laws.{OrderLaws, GroupLaws} - -class EitherTTests extends CatsSuite { - implicit val iso = CartesianTests.Isomorphisms.invariant[EitherT[ListWrapper, String, ?]](EitherT.catsDataFunctorForEitherT(ListWrapper.functor)) - - { - checkAll("EitherT[Option, ListWrapper[String], ?]", SemigroupKTests[EitherT[Option, ListWrapper[String], ?]].semigroupK[Int]) - checkAll("SemigroupK[EitherT[Option, ListWrapper[String], ?]]", SerializableTests.serializable(SemigroupK[EitherT[Option, ListWrapper[String], ?]])) - } - - { - implicit val F = ListWrapper.order[Either[String, Int]] - - checkAll("EitherT[List, String, Int]", OrderLaws[EitherT[ListWrapper, String, Int]].order) - checkAll("Order[EitherT[List, String, Int]]", SerializableTests.serializable(Order[EitherT[ListWrapper, String, Int]])) - } - - { - //If a Functor for F is defined - implicit val F = ListWrapper.functor - - checkAll("EitherT[ListWrapper, ?, ?]", BifunctorTests[EitherT[ListWrapper, ?, ?]].bifunctor[Int, Int, Int, String, String, String]) - checkAll("Bifunctor[EitherT[ListWrapper, ?, ?]]", SerializableTests.serializable(Bifunctor[EitherT[ListWrapper, ?, ?]])) - checkAll("EitherT[ListWrapper, Int, ?]", FunctorTests[EitherT[ListWrapper, Int, ?]].functor[Int, Int, Int]) - checkAll("Functor[EitherT[ListWrapper, Int, ?]]", SerializableTests.serializable(Functor[EitherT[ListWrapper, Int, ?]])) - } - - { - //If a Traverse for F is defined - implicit val F = ListWrapper.traverse - - checkAll("EitherT[ListWrapper, Int, ?]", TraverseTests[EitherT[ListWrapper, Int, ?]].traverse[Int, Int, Int, Int, Option, Option]) - checkAll("Traverse[EitherT[ListWrapper, Int, ?]]", SerializableTests.serializable(Traverse[EitherT[ListWrapper, Int, ?]])) - checkAll("EitherT[ListWrapper, ?, ?]", BitraverseTests[EitherT[ListWrapper, ?, ?]].bitraverse[Option, Int, Int, Int, String, String, String]) - checkAll("Bitraverse[EitherT[ListWrapper, ?, ?]]", SerializableTests.serializable(Bitraverse[EitherT[ListWrapper, ?, ?]])) - - } - - { - //if a Monad is defined - implicit val F = ListWrapper.monad - implicit val eq0 = EitherT.catsDataEqForEitherT[ListWrapper, String, Either[String, Int]] - implicit val eq1 = EitherT.catsDataEqForEitherT[EitherT[ListWrapper, String, ?], String, Int](eq0) - - Functor[EitherT[ListWrapper, String, ?]] - Applicative[EitherT[ListWrapper, String, ?]] - Monad[EitherT[ListWrapper, String, ?]] - - checkAll("EitherT[ListWrapper, String, Int]", MonadErrorTests[EitherT[ListWrapper, String, ?], String].monadError[Int, Int, Int]) - checkAll("MonadError[EitherT[List, ?, ?]]", SerializableTests.serializable(MonadError[EitherT[ListWrapper, String, ?], String])) - } - - { - //if a Monad is defined - implicit val F = ListWrapper.monad - - Functor[EitherT[ListWrapper, String, ?]] - Applicative[EitherT[ListWrapper, String, ?]] - Monad[EitherT[ListWrapper, String, ?]] - - checkAll("EitherT[ListWrapper, String, Int]", MonadTests[EitherT[ListWrapper, String, ?]].monad[Int, Int, Int]) - checkAll("Monad[EitherT[ListWrapper, String, ?]]", SerializableTests.serializable(Monad[EitherT[ListWrapper, String, ?]])) - } - - { - //If a foldable is defined - implicit val F = ListWrapper.foldable - - checkAll("EitherT[ListWrapper, Int, ?]", FoldableTests[EitherT[ListWrapper, Int, ?]].foldable[Int, Int]) - checkAll("Foldable[EitherT[ListWrapper, Int, ?]]", SerializableTests.serializable(Foldable[EitherT[ListWrapper, Int, ?]])) - } - - { - implicit val F = ListWrapper.partialOrder[Either[String, Int]] - - checkAll("EitherT[ListWrapper, String, Int]", OrderLaws[EitherT[ListWrapper, String, Int]].partialOrder) - checkAll("PartialOrder[EitherT[ListWrapper, String, Int]]", SerializableTests.serializable(PartialOrder[EitherT[ListWrapper, String, Int]])) - } - - { - implicit val F = ListWrapper.semigroup[Either[String, Int]] - - checkAll("EitherT[ListWrapper, String, Int]", GroupLaws[EitherT[ListWrapper, String, Int]].semigroup) - checkAll("Semigroup[EitherT[ListWrapper, String, Int]]", SerializableTests.serializable(Semigroup[EitherT[ListWrapper, String, Int]])) - } - - { - implicit val F = ListWrapper.monoid[Either[String, Int]] - - Semigroup[EitherT[ListWrapper, String, Int]] - - checkAll("EitherT[ListWrapper, String, Int]", GroupLaws[EitherT[ListWrapper, String, Int]].monoid) - checkAll("Monoid[EitherT[ListWrapper, String, Int]]", SerializableTests.serializable(Monoid[EitherT[ListWrapper, String, Int]])) - } - - { - implicit val F = ListWrapper.eqv[Either[String, Int]] - - checkAll("EitherT[ListWrapper, String, Int]", OrderLaws[EitherT[ListWrapper, String, Int]].eqv) - checkAll("Eq[EitherT[ListWrapper, String, Int]]", SerializableTests.serializable(Eq[EitherT[ListWrapper, String, Int]])) - } - - test("toValidated") { - forAll { (eithert: EitherT[List, String, Int]) => - eithert.toValidated.map(_.toEither) should === (eithert.value) - } - } - - test("toValidatedNel") { - forAll { (eithert: EitherT[List, String, Int]) => - eithert.toValidatedNel.map(_.toEither.leftMap(_.head)) should === (eithert.value) - } - } - - test("withValidated") { - forAll { (eithert: EitherT[List, String, Int], f: String => Char, g: Int => Double) => - eithert.withValidated(_.bimap(f, g)) should === (eithert.bimap(f, g)) - } - } - - test("fromEither") { - forAll { (either: Either[String, Int]) => - Some(either.isLeft) should === (EitherT.fromEither[Option](either).isLeft) - } - } - - test("isLeft negation of isRight") { - forAll { (eithert: EitherT[List, String, Int]) => - eithert.isLeft should === (eithert.isRight.map(! _)) - } - } - - test("double swap is noop") { - forAll { (eithert: EitherT[List, String, Int]) => - eithert.swap.swap should === (eithert) - } - } - - test("swap negates isRight") { - forAll { (eithert: EitherT[List, String, Int]) => - eithert.swap.isRight should === (eithert.isRight.map(! _)) - } - } - - test("toOption on Right returns Some") { - forAll { (eithert: EitherT[List, String, Int]) => - eithert.toOption.isDefined should === (eithert.isRight) - } - } - - test("toEither preserves isRight") { - forAll { (eithert: EitherT[List, String, Int]) => - eithert.value.map(_.isRight) should === (eithert.isRight) - } - } - - test("recover recovers handled values") { - val eithert = EitherT.left[Id, String, Int]("eithert") - eithert.recover { case "eithert" => 5 }.isRight should === (true) - } - - test("recover ignores unhandled values") { - val eithert = EitherT.left[Id, String, Int]("eithert") - eithert.recover { case "noteithert" => 5 } should === (eithert) - } - - test("recover ignores the right side") { - val eithert = EitherT.right[Id, String, Int](10) - eithert.recover { case "eithert" => 5 } should === (eithert) - } - - test("recoverWith recovers handled values") { - val eithert = EitherT.left[Id, String, Int]("eithert") - eithert.recoverWith { case "eithert" => EitherT.right[Id, String, Int](5) }.isRight should === (true) - } - - test("recoverWith ignores unhandled values") { - val eithert = EitherT.left[Id, String, Int]("eithert") - eithert.recoverWith { case "noteithert" => EitherT.right[Id, String, Int](5) } should === (eithert) - } - - test("recoverWith ignores the right side") { - val eithert = EitherT.right[Id, String, Int](10) - eithert.recoverWith { case "eithert" => EitherT.right[Id, String, Int](5) } should === (eithert) - } - - test("transform consistent with value.map") { - forAll { (eithert: EitherT[List, String, Int], f: Either[String, Int] => Either[Long, Double]) => - eithert.transform(f) should === (EitherT(eithert.value.map(f))) - } - } - - test("semiflatMap consistent with value.flatMap+f+pure") { - forAll { (eithert: EitherT[List, String, Int], f: Int => List[String]) => - eithert.semiflatMap(f) should === (EitherT(eithert.value.flatMap { - case l @ Left(_) => List(l.asInstanceOf[Either[String, String]]) - case Right(b) => f(b).map(Right(_)) - })) - } - } - - test("subflatMap consistent with value.map+flatMap") { - forAll { (eithert: EitherT[List, String, Int], f: Int => Either[String, Double]) => - eithert.subflatMap(f) should === (EitherT(eithert.value.map(_.flatMap(f)))) - } - } - - test("fold with Id consistent with Either fold") { - forAll { (eithert: EitherT[Id, String, Int], f: String => Long, g: Int => Long) => - eithert.fold(f, g) should === (eithert.value.fold(f, g)) - } - } - - test("valueOr with Id consistent with Either valueOr") { - forAll { (eithert: EitherT[Id, String, Int], f: String => Int) => - eithert.valueOr(f) should === (eithert.value.valueOr(f)) - } - } - - test("getOrElse with Id consistent with Either getOrElse") { - forAll { (eithert: EitherT[Id, String, Int], i: Int) => - eithert.getOrElse(i) should === (eithert.value.getOrElse(i)) - } - } - - test("getOrElseF with Id consistent with Either getOrElse") { - forAll { (eithert: EitherT[Id, String, Int], i: Int) => - eithert.getOrElseF(i) should === (eithert.value.getOrElse(i)) - } - } - - test("orElse with Id consistent with Either orElse") { - forAll { (eithert: EitherT[Id, String, Int], fallback: EitherT[Id, String, Int]) => - eithert.orElse(fallback).value should === (eithert.value.orElse(fallback.value)) - } - } - - test("orElse evaluates effect only once") { - forAll { (either: Either[String, Int], fallback: EitherT[Eval, String, Int]) => - var evals = 0 - val eithert = (EitherT(Eval.always { evals += 1; either }) orElse fallback) - eithert.value.value - evals should === (1) - } - } - - test("forall with Id consistent with Either forall") { - forAll { (eithert: EitherT[Id, String, Int], f: Int => Boolean) => - eithert.forall(f) should === (eithert.value.forall(f)) - } - } - - test("exists with Id consistent with Either exists") { - forAll { (eithert: EitherT[Id, String, Int], f: Int => Boolean) => - eithert.exists(f) should === (eithert.value.exists(f)) - } - } - - test("leftMap with Id consistent with Either leftMap") { - forAll { (eithert: EitherT[Id, String, Int], f: String => Long) => - eithert.leftMap(f).value should === (eithert.value.leftMap(f)) - } - } - - test("compare with Id consistent with Either compare") { - forAll { (x: EitherT[Id, String, Int], y: EitherT[Id, String, Int]) => - x.compare(y) should === (x.value.compare(y.value)) - } - } - - test("=== with Id consistent with Either ===") { - forAll { (x: EitherT[Id, String, Int], y: EitherT[Id, String, Int]) => - x === y should === (x.value === y.value) - } - } - - test("traverse with Id consistent with Either traverse") { - forAll { (x: EitherT[Id, String, Int], f: Int => Option[Long]) => - val e: Either[String, Int] = x.value - x.traverse(f).map(_.value) should === (e.traverse(f)) - } - } - - test("foldLeft with Id consistent with Either foldLeft") { - forAll { (x: EitherT[Id, String, Int], l: Long, f: (Long, Int) => Long) => - x.foldLeft(l)(f) should === (x.value.foldLeft(l)(f)) - } - } - - test("foldRight with Id consistent with Either foldRight") { - forAll { (x: EitherT[Id, String, Int], l: Eval[Long], f: (Int, Eval[Long]) => Eval[Long]) => - x.foldRight(l)(f) should === (x.value.foldRight(l)(f)) - } - } - - test("merge with Id consistent with Either merge") { - forAll { (x: EitherT[Id, Int, Int]) => - x.merge should === (x.value.merge) - } - } - - test("to consistent with toOption") { - forAll { (x: EitherT[List, String, Int]) => - x.to[Option] should === (x.toOption.value) - } - } - - test("toEither consistent with toOption") { - forAll { (x: EitherT[List, String, Int]) => - x.value.map(_.right.toOption) should === (x.toOption.value) - } - } - - test("ensure on left is identity") { - forAll { (x: EitherT[Id, String, Int], s: String, p: Int => Boolean) => - if (x.isLeft) { - x.ensure(s)(p) should === (x) - } - } - } - - test("ensure on right is identity if predicate satisfied") { - forAll { (x: EitherT[Id, String, Int], s: String, p: Int => Boolean) => - if (x.isRight && p(x getOrElse 0)) { - x.ensure(s)(p) should === (x) - } - } - } - - test("ensure should fail if predicate not satisfied") { - forAll { (x: EitherT[Id, String, Int], s: String, p: Int => Boolean) => - if (x.isRight && !p(x getOrElse 0)) { - x.ensure(s)(p) should === (EitherT.left[Id, String, Int](s)) - } - } - } -} diff --git a/tests/src/test/scala/cats/tests/EitherTests.scala b/tests/src/test/scala/cats/tests/EitherTests.scala index 5db908b543..97d7d74e08 100644 --- a/tests/src/test/scala/cats/tests/EitherTests.scala +++ b/tests/src/test/scala/cats/tests/EitherTests.scala @@ -1,23 +1,18 @@ package cats package tests -import cats.data.EitherT -import cats.laws.discipline._ -import cats.kernel.laws.{GroupLaws, OrderLaws} -import scala.util.Try +import cats.laws.discipline.{BitraverseTests, TraverseTests, MonadTests, SerializableTests, CartesianTests} +import cats.kernel.laws.OrderLaws class EitherTests extends CatsSuite { - implicit val iso = CartesianTests.Isomorphisms.invariant[Either[Int, ?]] - checkAll("Either[String, Int]", GroupLaws[Either[String, Int]].monoid) + implicit val iso = CartesianTests.Isomorphisms.invariant[Either[Int, ?]] checkAll("Either[Int, Int]", CartesianTests[Either[Int, ?]].cartesian[Int, Int, Int]) checkAll("Cartesian[Either[Int, ?]]", SerializableTests.serializable(Cartesian[Either[Int, ?]])) - implicit val eq0 = EitherT.catsDataEqForEitherT[Either[Int, ?], Int, Int] - - checkAll("Either[Int, Int]", MonadErrorTests[Either[Int, ?], Int].monadError[Int, Int, Int]) - checkAll("MonadError[Either[Int, ?]]", SerializableTests.serializable(MonadError[Either[Int, ?], Int])) + checkAll("Either[Int, Int]", MonadTests[Either[Int, ?]].monad[Int, Int, Int]) + checkAll("Monad[Either[Int, ?]]", SerializableTests.serializable(Monad[Either[Int, ?]])) checkAll("Either[Int, Int] with Option", TraverseTests[Either[Int, ?]].traverse[Int, Int, Int, Int, Option, Option]) checkAll("Traverse[Either[Int, ?]", SerializableTests.serializable(Traverse[Either[Int, ?]])) @@ -25,9 +20,6 @@ class EitherTests extends CatsSuite { checkAll("Either[?, ?]", BitraverseTests[Either].bitraverse[Option, Int, Int, Int, String, String, String]) checkAll("Bitraverse[Either]", SerializableTests.serializable(Bitraverse[Either])) - checkAll("Either[ListWrapper[String], ?]", SemigroupKTests[Either[ListWrapper[String], ?]].semigroupK[Int]) - checkAll("SemigroupK[Either[ListWrapper[String], ?]]", SerializableTests.serializable(SemigroupK[Either[ListWrapper[String], ?]])) - val partialOrder = catsStdPartialOrderForEither[Int, String] val order = implicitly[Order[Either[Int, String]]] val monad = implicitly[Monad[Either[Int, ?]]] @@ -44,18 +36,6 @@ class EitherTests extends CatsSuite { checkAll("Either[Int, String]", orderLaws.partialOrder(partialOrder)) checkAll("Either[Int, String]", orderLaws.order(order)) - test("Left/Right cast syntax") { - forAll { (e: Either[Int, String]) => - e match { - case l @ Left(_) => - l.rightCast[Double]: Either[Int, Double] - assert(true) - case r @ Right(_) => - r.leftCast[List[Byte]]: Either[List[Byte], String] - assert(true) - } - } - } test("implicit instances resolve specifically") { val eq = catsStdEqForEither[Int, String] @@ -75,177 +55,4 @@ class EitherTests extends CatsSuite { val x: Either[String, Int] = Left("l") x.map2Eval(bomb)(_ + _).value should === (x) } - - test("catchOnly lets non-matching exceptions escape") { - val _ = intercept[NumberFormatException] { - Either.catchOnly[IndexOutOfBoundsException]{ "foo".toInt } - } - } - - test("catchNonFatal catches non-fatal exceptions") { - assert(Either.catchNonFatal{ "foo".toInt }.isLeft) - assert(Either.catchNonFatal{ throw new Throwable("blargh") }.isLeft) - } - - test("fromTry is left for failed Try") { - forAll { t: Try[Int] => - t.isFailure should === (Either.fromTry(t).isLeft) - } - } - - test("fromOption isLeft consistent with Option.isEmpty") { - forAll { (o: Option[Int], s: String) => - Either.fromOption(o, s).isLeft should === (o.isEmpty) - } - } - - test("double swap is identity") { - forAll { (x: Either[Int, String]) => - x.swap.swap should === (x) - } - } - - test("swap negates isLeft/isRight") { - forAll { (x: Either[Int, String]) => - x.isLeft should !== (x.swap.isLeft) - x.isRight should !== (x.swap.isRight) - } - } - - test("isLeft consistent with isRight") { - forAll { (x: Either[Int, String]) => - x.isLeft should !== (x.isRight) - } - } - - test("foreach is noop for left") { - forAll { (x: Either[Int, String]) => - var count = 0 - x.foreach{ _ => count += 1} - (count == 0) should === (x.isLeft) - } - } - - test("getOrElse ignores default for right") { - forAll { (x: Either[Int, String], s: String, t: String) => - if (x.isRight) { - x.getOrElse(s) should === (x.getOrElse(t)) - } - } - } - - test("orElse") { - forAll { (x: Either[Int, String], y: Either[Int, String]) => - val z = x.orElse(y) - (z === (x)) || (z === (y)) should === (true) - } - } - - test("recover recovers handled values") { - val either = Either.left[String, Int]("either") - either.recover { case "either" => 5 }.isRight should === (true) - } - - test("recover ignores unhandled values") { - val either = Either.left[String, Int]("either") - either.recover { case "noteither" => 5 } should === (either) - } - - test("recover ignores the right side") { - val either = Either.right[String, Int](10) - either.recover { case "either" => 5 } should === (either) - } - - test("recoverWith recovers handled values") { - val either = Either.left[String, Int]("either") - either.recoverWith { case "either" => Either.right[String, Int](5) }.isRight should === (true) - } - - test("recoverWith ignores unhandled values") { - val either = Either.left[String, Int]("either") - either.recoverWith { case "noteither" => Either.right[String, Int](5) } should === (either) - } - - test("recoverWith ignores the right side") { - val either = Either.right[String, Int](10) - either.recoverWith { case "either" => Either.right[String, Int](5) } should === (either) - } - - test("valueOr consistent with swap then map then merge") { - forAll { (x: Either[Int, String], f: Int => String) => - x.valueOr(f) should === (x.swap.map(f).merge) - } - } - - test("isLeft implies forall") { - forAll { (x: Either[Int, String], p: String => Boolean) => - if (x.isLeft) { - x.forall(p) should === (true) - } - } - } - - test("isLeft implies exists is false") { - forAll { (x: Either[Int, String], p: String => Boolean) => - if (x.isLeft) { - x.exists(p) should === (false) - } - } - } - - test("ensure on left is identity") { - forAll { (x: Either[Int, String], i: Int, p: String => Boolean) => - if (x.isLeft) { - x.ensure(i)(p) should === (x) - } - } - } - - test("toIor then toEither is identity") { - forAll { (x: Either[Int, String]) => - x.toIor.toEither should === (x) - } - } - - test("toTry then fromTry is identity") { - implicit def eqTh: Eq[Throwable] = Eq.allEqual - - forAll { (x: Throwable Either String) => - Either.fromTry(x.toTry) should === (x) - } - } - - test("isLeft consistency") { - forAll { (x: Either[Int, String]) => - x.isLeft should === (x.toOption.isEmpty) - x.isLeft should === (x.toList.isEmpty) - x.isLeft should === (x.toValidated.isInvalid) - x.isLeft should === (x.toValidatedNel.isInvalid) - Option(x.isLeft) should === (x.toEitherT[Option].isLeft) - } - } - - test("withValidated") { - forAll { (x: Either[Int, String], f: Int => Double) => - x.withValidated(_.bimap(f, identity)) should === (x.leftMap(f)) - } - } - - test("combine is right iff both operands are right") { - forAll { (x: Either[Int, String], y: Either[Int, String]) => - x.combine(y).isRight should === (x.isRight && y.isRight) - } - } - - test("to consistent with toList") { - forAll { (x: Either[Int, String]) => - x.to[List] should === (x.toList) - } - } - - test("to consistent with toOption") { - forAll { (x: Either[Int, String]) => - x.to[Option] should === (x.toOption) - } - } } diff --git a/tests/src/test/scala/cats/tests/IorTests.scala b/tests/src/test/scala/cats/tests/IorTests.scala index ad79362dc5..eca8a7c9c9 100644 --- a/tests/src/test/scala/cats/tests/IorTests.scala +++ b/tests/src/test/scala/cats/tests/IorTests.scala @@ -1,7 +1,7 @@ package cats package tests -import cats.data.Ior +import cats.data.{Xor, Ior} import cats.laws.discipline.{BifunctorTests, TraverseTests, MonadTests, SerializableTests, CartesianTests} import cats.laws.discipline.arbitrary._ import org.scalacheck.Arbitrary._ @@ -34,7 +34,7 @@ class IorTests extends CatsSuite { test("onlyLeftOrRight") { forAll { (i: Int Ior String) => - i.onlyLeft.map(Left(_)).orElse(i.onlyRight.map(Right(_))) should === (i.onlyLeftOrRight) + i.onlyLeft.map(Xor.left).orElse(i.onlyRight.map(Xor.right)) should === (i.onlyLeftOrRight) } } @@ -147,9 +147,9 @@ class IorTests extends CatsSuite { } } - test("toEither consistent with right") { + test("toXor consistent with right") { forAll { (x: Int Ior String) => - x.toEither.toOption should === (x.right) + x.toXor.toOption should === (x.right) } } diff --git a/tests/src/test/scala/cats/tests/KleisliTests.scala b/tests/src/test/scala/cats/tests/KleisliTests.scala index 6153e2bb0c..451a80685c 100644 --- a/tests/src/test/scala/cats/tests/KleisliTests.scala +++ b/tests/src/test/scala/cats/tests/KleisliTests.scala @@ -2,7 +2,7 @@ package cats package tests import cats.arrow.{Arrow, Choice, Split, FunctionK} -import cats.data.{EitherT, Kleisli, Reader} +import cats.data.{XorT, Kleisli, Reader} import cats.functor.{Contravariant, Strong} import cats.laws.discipline._ import cats.laws.discipline.arbitrary._ @@ -15,7 +15,7 @@ class KleisliTests extends CatsSuite { implicit def kleisliEq[F[_], A, B](implicit A: Arbitrary[A], FB: Eq[F[B]]): Eq[Kleisli[F, A, B]] = Eq.by[Kleisli[F, A, B], A => F[B]](_.run) - implicit val eitherTEq = EitherT.catsDataEqForEitherT[Kleisli[Option, Int, ?], Unit, Int] + implicit val xorTEq = XorT.catsDataEqForXorT[Kleisli[Option, Int, ?], Unit, Int] implicit val iso = CartesianTests.Isomorphisms.invariant[Kleisli[Option, Int, ?]] @@ -207,4 +207,4 @@ class KleisliTests extends CatsSuite { FlatMap[IntReader] Semigroup[IntReader[String]] } -} +} \ No newline at end of file diff --git a/tests/src/test/scala/cats/tests/ListWrapper.scala b/tests/src/test/scala/cats/tests/ListWrapper.scala index 4c2dad7efa..d3e6530d4c 100644 --- a/tests/src/test/scala/cats/tests/ListWrapper.scala +++ b/tests/src/test/scala/cats/tests/ListWrapper.scala @@ -1,6 +1,7 @@ package cats package tests +import cats.data.Xor import cats.functor.Invariant import cats.instances.list._ @@ -90,7 +91,7 @@ object ListWrapper { def combineK[A](x: ListWrapper[A], y: ListWrapper[A]): ListWrapper[A] = ListWrapper(M.combineK(x.list, y.list)) - def tailRecM[A, B](a: A)(f: A => ListWrapper[Either[A,B]]): ListWrapper[B] = + def tailRecM[A, B](a: A)(f: A => ListWrapper[Xor[A,B]]): ListWrapper[B] = ListWrapper(M.tailRecM(a)(a => f(a).list)) } } diff --git a/tests/src/test/scala/cats/tests/MonadCombineTests.scala b/tests/src/test/scala/cats/tests/MonadCombineTests.scala index c9f11e234e..6a4901b10a 100644 --- a/tests/src/test/scala/cats/tests/MonadCombineTests.scala +++ b/tests/src/test/scala/cats/tests/MonadCombineTests.scala @@ -1,11 +1,14 @@ package cats package tests +import cats.data.Xor +import cats.laws.discipline.arbitrary.catsLawsArbitraryForXor + class MonadCombineTest extends CatsSuite { test("separate") { - forAll { (list: List[Either[Int, String]]) => - val ints = list.collect { case Left(i) => i } - val strings = list.collect { case Right(s) => s } + forAll { (list: List[Xor[Int, String]]) => + val ints = list.collect { case Xor.Left(i) => i } + val strings = list.collect { case Xor.Right(s) => s } val expected = (ints, strings) MonadCombine[List].separate(list) should === (expected) diff --git a/tests/src/test/scala/cats/tests/MonadRecInstancesTests.scala b/tests/src/test/scala/cats/tests/MonadRecInstancesTests.scala index 35afb2d594..6c94a95d3d 100644 --- a/tests/src/test/scala/cats/tests/MonadRecInstancesTests.scala +++ b/tests/src/test/scala/cats/tests/MonadRecInstancesTests.scala @@ -3,7 +3,6 @@ package tests import cats.data.{ Cokleisli, - EitherT, IdT, Ior, Kleisli, @@ -20,7 +19,7 @@ import cats.data.{ class MonadRecInstancesTests extends CatsSuite { def tailRecMStackSafety[M[_]: RecursiveTailRecM](implicit M: Monad[M], Eq: Eq[M[Int]]): Unit = { val n = 50000 - val res = M.tailRecM(0)(i => M.pure(if (i < n) Either.left(i + 1) else Either.right(i))) + val res = M.tailRecM(0)(i => M.pure(if (i < n) Xor.left(i + 1) else Xor.right(i))) res should === (M.pure(n)) } @@ -80,10 +79,6 @@ class MonadRecInstancesTests extends CatsSuite { tailRecMStackSafety[XorT[Option, String, ?]] } - test("tailRecM stack-safety for EitherT") { - tailRecMStackSafety[EitherT[Option, String, ?]] - } - test("tailRecM stack-safety for Ior") { tailRecMStackSafety[Int Ior ?] } diff --git a/tests/src/test/scala/cats/tests/OptionTTests.scala b/tests/src/test/scala/cats/tests/OptionTTests.scala index 041d72d0e0..8477553788 100644 --- a/tests/src/test/scala/cats/tests/OptionTTests.scala +++ b/tests/src/test/scala/cats/tests/OptionTTests.scala @@ -1,7 +1,7 @@ package cats package tests -import cats.data.{EitherT, OptionT} +import cats.data.{OptionT, Xor, XorT} import cats.kernel.laws.{GroupLaws, OrderLaws} import cats.laws.discipline._ import cats.laws.discipline.arbitrary._ @@ -68,32 +68,32 @@ class OptionTTests extends CatsSuite { { // F has a MonadError - type SEither[A] = Either[String, A] + type SXor[A] = String Xor A - implicit val monadError = OptionT.catsDataMonadErrorForOptionT[SEither, String] + implicit val monadError = OptionT.catsDataMonadErrorForOptionT[SXor, String] import org.scalacheck.Arbitrary - implicit val arb1 = implicitly[Arbitrary[OptionT[SEither, Int]]] - implicit val arb2 = implicitly[Arbitrary[OptionT[SEither, Int => Int]]] + implicit val arb1 = implicitly[Arbitrary[OptionT[SXor, Int]]] + implicit val arb2 = implicitly[Arbitrary[OptionT[SXor, Int => Int]]] - implicit val eq0 = OptionT.catsDataEqForOptionT[SEither, Option[Int]] - implicit val eq1 = OptionT.catsDataEqForOptionT[SEither, Int] - implicit val eq2 = OptionT.catsDataEqForOptionT[SEither, Unit] - implicit val eq3 = OptionT.catsDataEqForOptionT[SEither, SEither[Unit]] - implicit val eq4 = OptionT.catsDataEqForOptionT[SEither, SEither[Int]] - implicit val eq5 = EitherT.catsDataEqForEitherT[OptionT[SEither, ?], String, Int] - implicit val eq6 = OptionT.catsDataEqForOptionT[SEither, (Int, Int, Int)] + implicit val eq0 = OptionT.catsDataEqForOptionT[SXor, Option[Int]] + implicit val eq1 = OptionT.catsDataEqForOptionT[SXor, Int] + implicit val eq2 = OptionT.catsDataEqForOptionT[SXor, Unit] + implicit val eq3 = OptionT.catsDataEqForOptionT[SXor, SXor[Unit]] + implicit val eq4 = OptionT.catsDataEqForOptionT[SXor, SXor[Int]] + implicit val eq5 = XorT.catsDataEqForXorT[OptionT[SXor, ?], String, Int] + implicit val eq6 = OptionT.catsDataEqForOptionT[SXor, (Int, Int, Int)] - implicit val iso = CartesianTests.Isomorphisms.invariant[OptionT[SEither, ?]] + implicit val iso = CartesianTests.Isomorphisms.invariant[OptionT[SXor, ?]] - checkAll("OptionT[Either[String, ?], Int]", MonadErrorTests[OptionT[SEither, ?], String].monadError[Int, Int, Int]) - checkAll("MonadError[OptionT[Either[String, ?], ?]]", SerializableTests.serializable(monadError)) + checkAll("OptionT[String Xor ?, Int]", MonadErrorTests[OptionT[SXor, ?], String].monadError[Int, Int, Int]) + checkAll("MonadError[OptionT[String Xor ?, ?]]", SerializableTests.serializable(monadError)) - Monad[OptionT[SEither, ?]] - FlatMap[OptionT[SEither, ?]] - Applicative[OptionT[SEither, ?]] - Apply[OptionT[SEither, ?]] - Functor[OptionT[SEither, ?]] + Monad[OptionT[SXor, ?]] + FlatMap[OptionT[SXor, ?]] + Applicative[OptionT[SXor, ?]] + Apply[OptionT[SXor, ?]] + Functor[OptionT[SXor, ?]] } { @@ -219,9 +219,9 @@ class OptionTTests extends CatsSuite { } } - test("OptionT[Id, A].toRight consistent with Either.fromOption") { + test("OptionT[Id, A].toRight consistent with Xor.fromOption") { forAll { (o: OptionT[Id, Int], s: String) => - o.toRight(s).value should === (Either.fromOption(o.value, s)) + o.toRight(s).value should === (Xor.fromOption(o.value, s)) } } @@ -256,8 +256,8 @@ class OptionTTests extends CatsSuite { } test("show") { - val either: Either[String, Option[Int]] = Either.right(Some(1)) - OptionT[Either[String, ?], Int](either).show should === ("Right(Some(1))") + val xor: Xor[String, Option[Int]] = Xor.right(Some(1)) + OptionT[Xor[String, ?], Int](xor).show should === ("Xor.Right(Some(1))") } test("none") { diff --git a/tests/src/test/scala/cats/tests/RegressionTests.scala b/tests/src/test/scala/cats/tests/RegressionTests.scala index 50f000c87a..35fb976ead 100644 --- a/tests/src/test/scala/cats/tests/RegressionTests.scala +++ b/tests/src/test/scala/cats/tests/RegressionTests.scala @@ -1,7 +1,7 @@ package cats package tests -import cats.data.{Const, NonEmptyList} +import cats.data.{Const, NonEmptyList, Xor} import scala.collection.mutable @@ -20,10 +20,10 @@ class RegressionTests extends CatsSuite { implicit def instance[S]: Monad[State[S, ?]] = new Monad[State[S, ?]] { def pure[A](a: A): State[S, A] = State(s => (a, s)) def flatMap[A, B](sa: State[S, A])(f: A => State[S, B]): State[S, B] = sa.flatMap(f) - def tailRecM[A, B](a: A)(fn: A => State[S, Either[A, B]]): State[S, B] = + def tailRecM[A, B](a: A)(fn: A => State[S, Xor[A, B]]): State[S, B] = flatMap(fn(a)) { - case Left(a) => tailRecM(a)(fn) - case Right(b) => pure(b) + case Xor.Left(a) => tailRecM(a)(fn) + case Xor.Right(b) => pure(b) } } } @@ -89,11 +89,11 @@ class RegressionTests extends CatsSuite { ) } - test("#513: traverse short circuits - Either") { + test("#513: traverse short circuits - Xor") { var count = 0 - def validate(i: Int): Either[String, Int] = { + def validate(i: Int): Xor[String, Int] = { count = count + 1 - if (i < 5) Either.right(i) else Either.left(s"$i is greater than 5") + if (i < 5) Xor.right(i) else Xor.left(s"$i is greater than 5") } def checkAndResetCount(expected: Int): Unit = { @@ -101,31 +101,31 @@ class RegressionTests extends CatsSuite { count = 0 } - List(1,2,6,8).traverseU(validate) should === (Either.left("6 is greater than 5")) + List(1,2,6,8).traverseU(validate) should === (Xor.left("6 is greater than 5")) // shouldn't have ever evaluted validate(8) checkAndResetCount(3) - Stream(1,2,6,8).traverseU(validate) should === (Either.left("6 is greater than 5")) + Stream(1,2,6,8).traverseU(validate) should === (Xor.left("6 is greater than 5")) checkAndResetCount(3) type StringMap[A] = Map[String, A] val intMap: StringMap[Int] = Map("one" -> 1, "two" -> 2, "six" -> 6, "eight" -> 8) - intMap.traverseU(validate) should === (Either.left("6 is greater than 5")) + intMap.traverseU(validate) should === (Xor.left("6 is greater than 5")) checkAndResetCount(3) - NonEmptyList.of(1,2,6,8).traverseU(validate) should === (Either.left("6 is greater than 5")) + NonEmptyList.of(1,2,6,8).traverseU(validate) should === (Xor.left("6 is greater than 5")) checkAndResetCount(3) - NonEmptyList.of(6,8).traverseU(validate) should === (Either.left("6 is greater than 5")) + NonEmptyList.of(6,8).traverseU(validate) should === (Xor.left("6 is greater than 5")) checkAndResetCount(1) - List(1,2,6,8).traverseU_(validate) should === (Either.left("6 is greater than 5")) + List(1,2,6,8).traverseU_(validate) should === (Xor.left("6 is greater than 5")) checkAndResetCount(3) - NonEmptyList.of(1,2,6,7,8).traverseU_(validate) should === (Either.left("6 is greater than 5")) + NonEmptyList.of(1,2,6,7,8).traverseU_(validate) should === (Xor.left("6 is greater than 5")) checkAndResetCount(3) - NonEmptyList.of(6,7,8).traverseU_(validate) should === (Either.left("6 is greater than 5")) + NonEmptyList.of(6,7,8).traverseU_(validate) should === (Xor.left("6 is greater than 5")) checkAndResetCount(1) } } diff --git a/tests/src/test/scala/cats/tests/TransLiftTests.scala b/tests/src/test/scala/cats/tests/TransLiftTests.scala index befa32177c..fbc354b7d7 100644 --- a/tests/src/test/scala/cats/tests/TransLiftTests.scala +++ b/tests/src/test/scala/cats/tests/TransLiftTests.scala @@ -1,7 +1,7 @@ package cats package tests -import cats.data.{EitherT,OptionT,XorT,WriterT,Kleisli, StateT} +import data.{OptionT,XorT,WriterT,Kleisli, StateT} class TransLiftTests extends CatsSuite { @@ -22,8 +22,7 @@ class TransLiftTests extends CatsSuite { override def map[A, B](fa: JustAp[A])(f: A => B): JustAp[B] = JustAp(f(fa.a)) } - test("transLift for EitherT, XorT, OptionT, WriterT requires only Functor") { - val e: EitherT[JustFunctor, Int, Int] = JustFunctor(1).liftT[λ[(α[_], β) => EitherT[α, Int, β]]] + test("transLift for XorT, OptionT, WriterT requires only Functor") { val d: XorT[JustFunctor, Int, Int] = JustFunctor(1).liftT[λ[(α[_], β) => XorT[α, Int, β]]] val c: OptionT[JustFunctor, Int] = JustFunctor(1).liftT[OptionT] val a: WriterT[JustFunctor, Int, Int] = JustFunctor(1).liftT[λ[(α[_], β) => WriterT[α, Int, β]]] diff --git a/tests/src/test/scala/cats/tests/UnapplyTests.scala b/tests/src/test/scala/cats/tests/UnapplyTests.scala index 623dbd3381..6eccf76e18 100644 --- a/tests/src/test/scala/cats/tests/UnapplyTests.scala +++ b/tests/src/test/scala/cats/tests/UnapplyTests.scala @@ -14,8 +14,8 @@ class UnapplyTests extends CatsSuite { } test("Unapply works for F[_,_] with the left fixed") { - val x = Traverse[List].traverseU(List(1,2,3))(Either.right(_)) - (x: Either[String, List[Int]]) should === (Either.right(List(1,2,3))) + val x = Traverse[List].traverseU(List(1,2,3))(Xor.right(_)) + (x: String Xor List[Int]) should === (Xor.right(List(1,2,3))) } test("Unapply works for F[_[_],_] with the left fixed") { diff --git a/tests/src/test/scala/cats/tests/ValidatedTests.scala b/tests/src/test/scala/cats/tests/ValidatedTests.scala index d40ee2cf6e..266803d7a5 100644 --- a/tests/src/test/scala/cats/tests/ValidatedTests.scala +++ b/tests/src/test/scala/cats/tests/ValidatedTests.scala @@ -1,7 +1,7 @@ package cats package tests -import cats.data.{EitherT, NonEmptyList, Validated, ValidatedNel} +import cats.data.{XorT, NonEmptyList, Validated, ValidatedNel, Xor} import cats.data.Validated.{Valid, Invalid} import cats.laws.discipline.{BitraverseTests, TraverseTests, ApplicativeErrorTests, SerializableTests, CartesianTests} import org.scalacheck.Arbitrary._ @@ -19,10 +19,10 @@ class ValidatedTests extends CatsSuite { checkAll("Validated[?, ?]", BitraverseTests[Validated].bitraverse[Option, Int, Int, Int, String, String, String]) checkAll("Bitraverse[Validated]", SerializableTests.serializable(Bitraverse[Validated])) - implicit val eq0 = EitherT.catsDataEqForEitherT[Validated[String, ?], String, Int] + implicit val eq0 = XorT.catsDataEqForXorT[Validated[String, ?], String, Int] checkAll("Validated[String, Int]", ApplicativeErrorTests[Validated[String, ?], String].applicativeError[Int, Int, Int]) - checkAll("ApplicativeError[Validated, String]", SerializableTests.serializable(ApplicativeError[Validated[String, ?], String])) + checkAll("ApplicativeError[Xor, String]", SerializableTests.serializable(ApplicativeError[Validated[String, ?], String])) checkAll("Validated[String, Int] with Option", TraverseTests[Validated[String,?]].traverse[Int, Int, Int, Int, Option, Option]) checkAll("Traverse[Validated[String, ?]]", SerializableTests.serializable(Traverse[Validated[String,?]])) @@ -140,9 +140,9 @@ class ValidatedTests extends CatsSuite { } } - test("andThen consistent with Either's flatMap"){ + test("andThen consistent with Xor's flatMap"){ forAll { (v: Validated[String, Int], f: Int => Validated[String, Int]) => - v.andThen(f) should === (v.withEither(_.flatMap(f(_).toEither))) + v.andThen(f) should === (v.withXor(_.flatMap(f(_).toXor))) } } @@ -156,9 +156,9 @@ class ValidatedTests extends CatsSuite { (Validated.invalid("foo") andThen even) should === (Validated.invalid("foo")) } - test("fromOption consistent with Either.fromOption"){ + test("fromOption consistent with Xor.fromOption"){ forAll { (o: Option[Int], s: String) => - Validated.fromOption(o, s) should === (Either.fromOption(o, s).toValidated) + Validated.fromOption(o, s) should === (Xor.fromOption(o, s).toValidated) } } diff --git a/tests/src/test/scala/cats/tests/WriterTTests.scala b/tests/src/test/scala/cats/tests/WriterTTests.scala index 1f589afe97..ca610b8e77 100644 --- a/tests/src/test/scala/cats/tests/WriterTTests.scala +++ b/tests/src/test/scala/cats/tests/WriterTTests.scala @@ -1,7 +1,7 @@ package cats package tests -import cats.data.{EitherT, Validated, Writer, WriterT} +import cats.data.{Validated, Writer, WriterT, XorT} import cats.functor.{Bifunctor, Contravariant} import cats.laws.discipline._ import cats.laws.discipline.arbitrary._ @@ -348,8 +348,8 @@ class WriterTTests extends CatsSuite { implicit val iso = CartesianTests.Isomorphisms.invariant[WriterT[Validated[String, ?], ListWrapper[Int], ?]] implicit def eq1[A:Eq]: Eq[WriterT[Validated[String, ?], ListWrapper[Int], A]] = WriterT.catsDataEqForWriterT[Validated[String, ?], ListWrapper[Int], A] - implicit val eq2: Eq[EitherT[WriterT[Validated[String, ?], ListWrapper[Int], ?], String, Int]] = - EitherT.catsDataEqForEitherT[WriterT[Validated[String, ?], ListWrapper[Int], ?], String, Int] + implicit val eq2: Eq[XorT[WriterT[Validated[String, ?], ListWrapper[Int], ?], String, Int]] = + XorT.catsDataEqForXorT[WriterT[Validated[String, ?], ListWrapper[Int], ?], String, Int] implicit def arb0[A:Arbitrary]: Arbitrary[WriterT[Validated[String, ?], ListWrapper[Int], A]] = arbitrary.catsLawsArbitraryForWriterT[Validated[String, ?], ListWrapper[Int], A] @@ -365,7 +365,7 @@ class WriterTTests extends CatsSuite { // F has a MonadError and L has a Monoid implicit val L: Monoid[ListWrapper[Int]] = ListWrapper.monoid[Int] implicit val iso = CartesianTests.Isomorphisms.invariant[WriterT[Option, ListWrapper[Int], ?]] - implicit val eq0: Eq[EitherT[WriterT[Option, ListWrapper[Int], ?], Unit, Int]] = EitherT.catsDataEqForEitherT[WriterT[Option, ListWrapper[Int], ?], Unit, Int] + implicit val eq0: Eq[XorT[WriterT[Option, ListWrapper[Int], ?], Unit, Int]] = XorT.catsDataEqForXorT[WriterT[Option, ListWrapper[Int], ?], Unit, Int] Functor[WriterT[Option, ListWrapper[Int], ?]] diff --git a/tests/src/test/scala/cats/tests/XorTTests.scala b/tests/src/test/scala/cats/tests/XorTTests.scala index 4912d5f83c..edce0887b2 100644 --- a/tests/src/test/scala/cats/tests/XorTTests.scala +++ b/tests/src/test/scala/cats/tests/XorTTests.scala @@ -2,7 +2,7 @@ package cats package tests import cats.functor.Bifunctor -import cats.data.{EitherT, Xor, XorT} +import cats.data.{Xor, XorT} import cats.laws.discipline._ import cats.laws.discipline.arbitrary._ import cats.kernel.laws.{OrderLaws, GroupLaws} @@ -46,8 +46,8 @@ class XorTTests extends CatsSuite { { //if a Monad is defined implicit val F = ListWrapper.monad - implicit val eq0 = XorT.catsDataEqForXorT[ListWrapper, String, Either[String, Int]] - implicit val eq1 = EitherT.catsDataEqForEitherT[XorT[ListWrapper, String, ?], String, Int](eq0) + implicit val eq0 = XorT.catsDataEqForXorT[ListWrapper, String, String Xor Int] + implicit val eq1 = XorT.catsDataEqForXorT[XorT[ListWrapper, String, ?], String, Int](eq0) Functor[XorT[ListWrapper, String, ?]] Applicative[XorT[ListWrapper, String, ?]] diff --git a/tests/src/test/scala/cats/tests/XorTests.scala b/tests/src/test/scala/cats/tests/XorTests.scala index 10b60c16a4..8b110ed41b 100644 --- a/tests/src/test/scala/cats/tests/XorTests.scala +++ b/tests/src/test/scala/cats/tests/XorTests.scala @@ -1,7 +1,7 @@ package cats package tests -import cats.data.{EitherT, NonEmptyList, Xor} +import cats.data.{NonEmptyList, Xor, XorT} import cats.data.Xor._ import cats.laws.discipline.{SemigroupKTests} import cats.laws.discipline.arbitrary._ @@ -22,7 +22,7 @@ class XorTests extends CatsSuite { checkAll("Xor[String, NonEmptyList[Int]]", GroupLaws[Xor[String, NonEmptyList[Int]]].semigroup) - implicit val eq0 = EitherT.catsDataEqForEitherT[Xor[String, ?], String, Int] + implicit val eq0 = XorT.catsDataEqForXorT[Xor[String, ?], String, Int] checkAll("Xor[String, Int]", MonadErrorTests[Xor[String, ?], String].monadError[Int, Int, Int]) checkAll("MonadError[Xor, String]", SerializableTests.serializable(MonadError[Xor[String, ?], String]))