From a4a2844ab9545928524e0567dfd8070f5e414fc3 Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Sun, 15 Nov 2015 09:07:42 -0500 Subject: [PATCH 1/2] Add Traverse and Foldable instances for XorT --- core/src/main/scala/cats/data/XorT.scala | 24 +++++++++++++++++++ .../src/test/scala/cats/tests/XorTTests.scala | 19 +++++++++++---- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/core/src/main/scala/cats/data/XorT.scala b/core/src/main/scala/cats/data/XorT.scala index 3669119de7..b9ada5941c 100644 --- a/core/src/main/scala/cats/data/XorT.scala +++ b/core/src/main/scala/cats/data/XorT.scala @@ -171,6 +171,10 @@ private[data] abstract class XorTInstances extends XorTInstances1 { } } + implicit def xorTTraverse[F[_], L](implicit F: Traverse[F]): Traverse[XorT[F, L, ?]] = + new XorTTraverse[F, L] { + val F0: Traverse[F] = F + } } private[data] abstract class XorTInstances1 extends XorTInstances2 { @@ -191,6 +195,11 @@ private[data] abstract class XorTInstances1 extends XorTInstances2 { def empty[A]: XorT[F, L, A] = XorT.left(F.pure(L.empty))(F) } } + + implicit def xorTFoldable[F[_], L](implicit F: Foldable[F]): Foldable[XorT[F, L, ?]] = + new XorTFoldable[F, L] { + val F0: Foldable[F] = F + } } private[data] abstract class XorTInstances2 extends XorTInstances3 { @@ -264,4 +273,19 @@ private[data] trait XorTMonadCombine[F[_], L] extends MonadCombine[XorT[F, L, ?] implicit val L: Monoid[L] } +private[data] sealed trait XorTFoldable[F[_], L] extends Foldable[XorT[F, L, ?]] { + implicit def F0: Foldable[F] + + def foldLeft[A, B](fa: XorT[F, L, A], b: B)(f: (B, A) => B): B = + fa.foldLeft(b)(f) + + def foldRight[A, B](fa: XorT[F, L, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = + fa.foldRight(lb)(f) +} + +private[data] sealed trait XorTTraverse[F[_], L] extends Traverse[XorT[F, L, ?]] with XorTFoldable[F, L] { + override implicit def F0: Traverse[F] + override def traverse[G[_]: Applicative, A, B](fa: XorT[F, L, A])(f: A => G[B]): G[XorT[F, L, B]] = + fa traverse f +} diff --git a/tests/src/test/scala/cats/tests/XorTTests.scala b/tests/src/test/scala/cats/tests/XorTTests.scala index 79972bcc10..f3bf25d922 100644 --- a/tests/src/test/scala/cats/tests/XorTTests.scala +++ b/tests/src/test/scala/cats/tests/XorTTests.scala @@ -1,8 +1,9 @@ -package cats.tests +package cats +package tests -import cats.{Id, MonadError} +import cats.functor.Bifunctor import cats.data.{Xor, XorT} -import cats.laws.discipline.{BifunctorTests, MonadErrorTests, MonoidKTests, SerializableTests} +import cats.laws.discipline.{BifunctorTests, FoldableTests, MonadErrorTests, MonoidKTests, SerializableTests, TraverseTests} import cats.laws.discipline.arbitrary._ @@ -10,9 +11,19 @@ class XorTTests extends CatsSuite { implicit val eq0 = XorT.xorTEq[List, String, String Xor Int] implicit val eq1 = XorT.xorTEq[XorT[List, String, ?], String, Int](eq0) checkAll("XorT[List, String, Int]", MonadErrorTests[XorT[List, String, ?], String].monadError[Int, Int, Int]) - checkAll("XorT[List, String, Int]", MonoidKTests[XorT[List, String, ?]].monoidK[Int]) checkAll("MonadError[XorT[List, ?, ?]]", SerializableTests.serializable(MonadError[XorT[List, String, ?], String])) + checkAll("XorT[List, String, Int]", MonoidKTests[XorT[List, String, ?]].monoidK[Int]) + checkAll("MonoidK[XorT[List, String, ?]]", SerializableTests.serializable(MonoidK[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, Int, ?]", TraverseTests[XorT[List, Int, ?]].foldable[Int, Int]) + checkAll("Traverse[XorT[List, Int, ?]]", SerializableTests.serializable(Traverse[XorT[List, Int, ?]])) + + { + 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, ?]])) + } test("toValidated") { forAll { (xort: XorT[List, String, Int]) => From bab27335ea63912876450c08eb2e2e5202ebb137 Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Sun, 15 Nov 2015 09:10:39 -0500 Subject: [PATCH 2/2] Check Functor laws on XorT And make sure there isn't an ambiguous Functor instance --- tests/src/test/scala/cats/tests/XorTTests.scala | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/src/test/scala/cats/tests/XorTTests.scala b/tests/src/test/scala/cats/tests/XorTTests.scala index f3bf25d922..9cb70ec106 100644 --- a/tests/src/test/scala/cats/tests/XorTTests.scala +++ b/tests/src/test/scala/cats/tests/XorTTests.scala @@ -3,7 +3,7 @@ package tests import cats.functor.Bifunctor import cats.data.{Xor, XorT} -import cats.laws.discipline.{BifunctorTests, FoldableTests, MonadErrorTests, MonoidKTests, SerializableTests, TraverseTests} +import cats.laws.discipline.{BifunctorTests, FoldableTests, FunctorTests, MonadErrorTests, MonoidKTests, SerializableTests, TraverseTests} import cats.laws.discipline.arbitrary._ @@ -25,6 +25,16 @@ class XorTTests extends CatsSuite { checkAll("Foldable[XorT[ListWrapper, Int, ?]]", SerializableTests.serializable(Foldable[XorT[ListWrapper, Int, ?]])) } + { + implicit val F = ListWrapper.functor + checkAll("XorT[ListWrapper, Int, ?]", FunctorTests[XorT[ListWrapper, Int, ?]].functor[Int, Int, Int]) + checkAll("Functor[XorT[ListWrapper, Int, ?]]", SerializableTests.serializable(Functor[XorT[ListWrapper, 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)