diff --git a/core/src/main/scala/cats/data/Xor.scala b/core/src/main/scala/cats/data/Xor.scala index 857ef3c155..8e69ce0104 100644 --- a/core/src/main/scala/cats/data/Xor.scala +++ b/core/src/main/scala/cats/data/Xor.scala @@ -181,6 +181,8 @@ sealed abstract class Xor[+A, +B] extends Product with Serializable { a => s"Xor.Left(${AA.show(a)})", b => s"Xor.Right(${BB.show(b)})" ) + + def ap[AA >: A, BB >: B, C](that: AA Xor (BB => C)): AA Xor C = that.flatMap(this.map) } object Xor extends XorInstances with XorFunctions { @@ -242,6 +244,7 @@ private[data] sealed abstract class XorInstances extends XorInstances1 { def foldLeft[B, C](fa: A Xor B, c: C)(f: (C, B) => C): C = fa.foldLeft(c)(f) def foldRight[B, C](fa: A Xor B, lc: Eval[C])(f: (B, Eval[C]) => Eval[C]): Eval[C] = fa.foldRight(lc)(f) 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 (B Xor C)): A Xor C = f(b) match { diff --git a/core/src/main/scala/cats/data/XorT.scala b/core/src/main/scala/cats/data/XorT.scala index 8d8e819220..f0c9b31ecd 100644 --- a/core/src/main/scala/cats/data/XorT.scala +++ b/core/src/main/scala/cats/data/XorT.scala @@ -249,6 +249,9 @@ private[data] abstract class XorTInstances extends XorTInstances1 { XorT(Functor[M].map(ma)(Xor.right)) } + implicit def catsMonoidForXorT[F[_], L, A](implicit F: Monoid[F[L Xor A]]): Monoid[XorT[F, L, A]] = + new XorTMonoid[F, L, A] { implicit val F0 = F } + } private[data] abstract class XorTInstances1 extends XorTInstances2 { @@ -258,7 +261,10 @@ private[data] abstract class XorTInstances1 extends XorTInstances2 { implicit val L0 = L new XorTMonadFilter[F, L] { implicit val F = F0; implicit val L = L0 } } - */ + */ + + implicit def catsSemigroupForXorT[F[_], L, A](implicit F: Semigroup[F[L Xor A]]): Semigroup[XorT[F, L, A]] = + new XorTSemigroup[F, L, A] { implicit val F0 = F } implicit def catsDataFoldableForXorT[F[_], L](implicit F: Foldable[F]): Foldable[XorT[F, L, ?]] = new XorTFoldable[F, L] { @@ -282,19 +288,11 @@ private[data] abstract class XorTInstances2 extends XorTInstances3 { } private[data] abstract class XorTInstances3 extends XorTInstances4 { - implicit def catsDataMonadErrorForXorT[F[_], L](implicit F: Monad[F]): MonadError[XorT[F, L, ?], L] = { - implicit val F0 = F + implicit def catsDataMonadErrorForXorT[F[_], L](implicit F0: Monad[F]): MonadError[XorT[F, L, ?], L] = new XorTMonadError[F, L] { implicit val F = F0 } - } - implicit def catsDataSemigroupKForXorT[F[_], L](implicit F: Monad[F]): SemigroupK[XorT[F, L, ?]] = - new SemigroupK[XorT[F,L,?]] { - def combineK[A](x: XorT[F,L,A], y: XorT[F, L, A]): XorT[F, L, A] = - XorT(F.flatMap(x.value) { - case l @ Xor.Left(_) => y.value - case r @ Xor.Right(_) => F.pure(r) - }) - } + implicit def catsDataSemigroupKForXorT[F[_], L](implicit F0: Monad[F]): SemigroupK[XorT[F, L, ?]] = + new XorTSemigroupK[F, L] { implicit val F = F0 } implicit def catsDataEqForXorT[F[_], L, R](implicit F: Eq[F[L Xor R]]): Eq[XorT[F, L, R]] = new XorTEq[F, L, R] { @@ -303,10 +301,28 @@ private[data] abstract class XorTInstances3 extends XorTInstances4 { } private[data] abstract class XorTInstances4 { - implicit def catsDataFunctorForXorT[F[_], L](implicit F: Functor[F]): Functor[XorT[F, L, ?]] = { - implicit val F0 = F + implicit def catsDataFunctorForXorT[F[_], L](implicit F0: Functor[F]): Functor[XorT[F, L, ?]] = new XorTFunctor[F, L] { implicit val F = F0 } - } +} + +private[data] trait XorTSemigroup[F[_], L, A] extends Semigroup[XorT[F, L, A]] { + implicit val F0: Semigroup[F[L Xor A]] + def combine(x: XorT[F, L ,A], y: XorT[F, L , A]): XorT[F, L , A] = + XorT(F0.combine(x.value, y.value)) +} + +private[data] trait XorTMonoid[F[_], L, A] extends Monoid[XorT[F, L, A]] with XorTSemigroup[F, L, A] { + implicit val F0: Monoid[F[L Xor A]] + def empty: XorT[F, L, A] = XorT(F0.empty) +} + +private[data] trait XorTSemigroupK[F[_], L] extends SemigroupK[XorT[F, L, ?]] { + implicit val F: Monad[F] + def combineK[A](x: XorT[F,L,A], y: XorT[F, L, A]): XorT[F, L, A] = + XorT(F.flatMap(x.value) { + case l @ Xor.Left(_) => y.value + case r @ Xor.Right(_) => F.pure(r) + }) } private[data] trait XorTFunctor[F[_], L] extends Functor[XorT[F, L, ?]] { @@ -316,7 +332,7 @@ private[data] trait XorTFunctor[F[_], L] extends Functor[XorT[F, L, ?]] { private[data] trait XorTMonad[F[_], L] extends Monad[XorT[F, L, ?]] with XorTFunctor[F, L] { implicit val F: Monad[F] - def pure[A](a: A): XorT[F, L, A] = XorT.pure[F, L, A](a) + 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 } diff --git a/tests/src/test/scala/cats/tests/XorTTests.scala b/tests/src/test/scala/cats/tests/XorTTests.scala index 80c96a6601..678094946b 100644 --- a/tests/src/test/scala/cats/tests/XorTTests.scala +++ b/tests/src/test/scala/cats/tests/XorTTests.scala @@ -5,55 +5,108 @@ import cats.functor.Bifunctor import cats.data.{Xor, XorT} import cats.laws.discipline._ import cats.laws.discipline.arbitrary._ -import cats.kernel.laws.OrderLaws +import cats.kernel.laws.{OrderLaws, GroupLaws} class XorTTests extends CatsSuite { - implicit val eq0 = XorT.catsDataEqForXorT[List, String, String Xor Int] - implicit val eq1 = XorT.catsDataEqForXorT[XorT[List, String, ?], String, Int](eq0) - implicit val iso = CartesianTests.Isomorphisms.invariant[XorT[List, String, ?]] - checkAll("XorT[List, String, Int]", MonadErrorTests[XorT[List, String, ?], String].monadError[Int, Int, Int]) - checkAll("MonadError[XorT[List, ?, ?]]", SerializableTests.serializable(MonadError[XorT[List, String, ?], String])) - checkAll("XorT[List, String, Int]", MonadRecTests[XorT[List, String, ?]].monadRec[Int, Int, Int]) - checkAll("MonadRec[XorT[List, String, ?]]", SerializableTests.serializable(MonadRec[XorT[List, String, ?]])) - checkAll("XorT[List, ?, ?]", BifunctorTests[XorT[List, ?, ?]].bifunctor[Int, Int, Int, String, String, String]) - checkAll("Bifunctor[XorT[List, ?, ?]]", SerializableTests.serializable(Bifunctor[XorT[List, ?, ?]])) - checkAll("XorT[List, ?, ?]", BitraverseTests[XorT[List, ?, ?]].bitraverse[Option, Int, Int, Int, String, String, String]) - checkAll("Bitraverse[XorT[List, ?, ?]]", SerializableTests.serializable(Bitraverse[XorT[List, ?, ?]])) - checkAll("XorT[List, Int, ?]", TraverseTests[XorT[List, Int, ?]].traverse[Int, Int, Int, Int, Option, Option]) - checkAll("Traverse[XorT[List, Int, ?]]", SerializableTests.serializable(Traverse[XorT[List, Int, ?]])) - checkAll("XorT[List, String, Int]", OrderLaws[XorT[List, String, Int]].order) - checkAll("Order[XorT[List, String, Int]]", SerializableTests.serializable(Order[XorT[List, String, Int]])) - checkAll("XorT[Option, ListWrapper[String], ?]", SemigroupKTests[XorT[Option, ListWrapper[String], ?]].semigroupK[Int]) - checkAll("SemigroupK[XorT[Option, ListWrapper[String], ?]]", SerializableTests.serializable(SemigroupK[XorT[Option, ListWrapper[String], ?]])) + implicit val iso = CartesianTests.Isomorphisms.invariant[XorT[ListWrapper, String, ?]](XorT.catsDataFunctorForXorT(ListWrapper.functor)) { - implicit val F = ListWrapper.foldable - checkAll("XorT[ListWrapper, Int, ?]", FoldableTests[XorT[ListWrapper, Int, ?]].foldable[Int, Int]) - checkAll("Foldable[XorT[ListWrapper, Int, ?]]", SerializableTests.serializable(Foldable[XorT[ListWrapper, Int, ?]])) + checkAll("XorT[Option, ListWrapper[String], ?]", SemigroupKTests[XorT[Option, ListWrapper[String], ?]].semigroupK[Int]) + checkAll("SemigroupK[XorT[Option, ListWrapper[String], ?]]", SerializableTests.serializable(SemigroupK[XorT[Option, ListWrapper[String], ?]])) } { + implicit val F = ListWrapper.order[String Xor Int] + + checkAll("XorT[List, String, Int]", OrderLaws[XorT[ListWrapper, String, Int]].order) + checkAll("Order[XorT[List, String, Int]]", SerializableTests.serializable(Order[XorT[ListWrapper, String, Int]])) + } + + { + //If a Functor for F is defined implicit val F = ListWrapper.functor + + checkAll("XorT[ListWrapper, ?, ?]", BifunctorTests[XorT[ListWrapper, ?, ?]].bifunctor[Int, Int, Int, String, String, String]) + checkAll("Bifunctor[XorT[ListWrapper, ?, ?]]", SerializableTests.serializable(Bifunctor[XorT[ListWrapper, ?, ?]])) checkAll("XorT[ListWrapper, Int, ?]", FunctorTests[XorT[ListWrapper, Int, ?]].functor[Int, Int, Int]) checkAll("Functor[XorT[ListWrapper, Int, ?]]", SerializableTests.serializable(Functor[XorT[ListWrapper, Int, ?]])) } + { + //If a Traverse for F is defined + implicit val F = ListWrapper.traverse + + checkAll("XorT[ListWrapper, Int, ?]", TraverseTests[XorT[ListWrapper, Int, ?]].traverse[Int, Int, Int, Int, Option, Option]) + checkAll("Traverse[XorT[ListWrapper, Int, ?]]", SerializableTests.serializable(Traverse[XorT[ListWrapper, Int, ?]])) + checkAll("XorT[ListWrapper, ?, ?]", BitraverseTests[XorT[ListWrapper, ?, ?]].bitraverse[Option, Int, Int, Int, String, String, String]) + checkAll("Bitraverse[XorT[ListWrapper, ?, ?]]", SerializableTests.serializable(Bitraverse[XorT[ListWrapper, ?, ?]])) + + } + + { + //if a Monad is defined + implicit val F = ListWrapper.monad + 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, ?]] + Monad[XorT[ListWrapper, String, ?]] + + checkAll("XorT[ListWrapper, String, Int]", MonadErrorTests[XorT[ListWrapper, String, ?], String].monadError[Int, Int, Int]) + checkAll("MonadError[XorT[List, ?, ?]]", SerializableTests.serializable(MonadError[XorT[ListWrapper, String, ?], String])) + } + + { + //if a MonadRec is defined + implicit val F = ListWrapper.monadRec + + Functor[XorT[ListWrapper, String, ?]] + Applicative[XorT[ListWrapper, String, ?]] + Monad[XorT[ListWrapper, String, ?]] + + checkAll("XorT[ListWrapper, String, Int]", MonadRecTests[XorT[ListWrapper, String, ?]].monadRec[Int, Int, Int]) + checkAll("MonadRec[XorT[ListWrapper, String, ?]]", SerializableTests.serializable(MonadRec[XorT[ListWrapper, String, ?]])) + } + + { + //If a foldable is defined + implicit val F = ListWrapper.foldable + + checkAll("XorT[ListWrapper, Int, ?]", FoldableTests[XorT[ListWrapper, Int, ?]].foldable[Int, Int]) + checkAll("Foldable[XorT[ListWrapper, Int, ?]]", SerializableTests.serializable(Foldable[XorT[ListWrapper, Int, ?]])) + } + { implicit val F = ListWrapper.partialOrder[String Xor Int] + checkAll("XorT[ListWrapper, String, Int]", OrderLaws[XorT[ListWrapper, String, Int]].partialOrder) checkAll("PartialOrder[XorT[ListWrapper, String, Int]]", SerializableTests.serializable(PartialOrder[XorT[ListWrapper, String, Int]])) } + { + implicit val F = ListWrapper.semigroup[String Xor Int] + + checkAll("XorT[ListWrapper, String, Int]", GroupLaws[XorT[ListWrapper, String, Int]].semigroup) + checkAll("Semigroup[XorT[ListWrapper, String, Int]]", SerializableTests.serializable(Semigroup[XorT[ListWrapper, String, Int]])) + } + + { + implicit val F = ListWrapper.monoid[String Xor Int] + + Semigroup[XorT[ListWrapper, String, Int]] + + checkAll("XorT[ListWrapper, String, Int]", GroupLaws[XorT[ListWrapper, String, Int]].monoid) + checkAll("Monoid[XorT[ListWrapper, String, Int]]", SerializableTests.serializable(Monoid[XorT[ListWrapper, String, Int]])) + } + { implicit val F = ListWrapper.eqv[String Xor Int] + checkAll("XorT[ListWrapper, String, Int]", OrderLaws[XorT[ListWrapper, String, Int]].eqv) checkAll("Eq[XorT[ListWrapper, String, Int]]", SerializableTests.serializable(Eq[XorT[ListWrapper, String, Int]])) } - // make sure that the Monad and Traverse instances don't result in ambiguous - // Functor instances - Functor[XorT[List, Int, ?]] - test("toValidated") { forAll { (xort: XorT[List, String, Int]) => xort.toValidated.map(_.toXor) should === (xort.value)