From 5697f2f145d2ed8dbef7648c36360bf29f50c9c3 Mon Sep 17 00:00:00 2001 From: peterneyens Date: Sat, 22 Apr 2017 10:54:53 +0100 Subject: [PATCH] Improve test coverage `IdT` --- core/src/main/scala/cats/data/IdT.scala | 67 ++++++++++----- .../src/test/scala/cats/tests/IdTTests.scala | 86 ++++++++++++++++--- .../test/scala/cats/tests/ListWrapper.scala | 2 + 3 files changed, 122 insertions(+), 33 deletions(-) diff --git a/core/src/main/scala/cats/data/IdT.scala b/core/src/main/scala/cats/data/IdT.scala index 10051123653..a0f3f05a4bd 100644 --- a/core/src/main/scala/cats/data/IdT.scala +++ b/core/src/main/scala/cats/data/IdT.scala @@ -38,15 +38,24 @@ object IdT extends IdTInstances { private[data] sealed trait IdTFunctor[F[_]] extends Functor[IdT[F, ?]] { implicit val F0: Functor[F] - def map[A, B](fa: IdT[F, A])(f: A => B): IdT[F, B] = + override def map[A, B](fa: IdT[F, A])(f: A => B): IdT[F, B] = fa.map(f) } -private[data] sealed trait IdTMonad[F[_]] extends Monad[IdT[F, ?]] { - implicit val F0: Monad[F] +private[data] sealed trait IdTApply[F[_]] extends Apply[IdT[F, ?]] with IdTFunctor[F] { + implicit val F0: Apply[F] + + override def ap[A, B](ff: IdT[F, A => B])(fa: IdT[F, A]): IdT[F, B] = fa.ap(ff) +} - def pure[A](a: A): IdT[F, A] = - IdT.pure(a) +private[data] sealed trait IdTApplicative[F[_]] extends Applicative[IdT[F, ?]] with IdTApply[F] { + implicit val F0: Applicative[F] + + def pure[A](a: A): IdT[F, A] = IdT.pure(a) +} + +private[data] sealed trait IdTFlatMap[F[_]] extends FlatMap[IdT[F, ?]] with IdTApply[F] { + implicit val F0: FlatMap[F] def flatMap[A, B](fa: IdT[F, A])(f: A => IdT[F, B]): IdT[F, B] = fa.flatMap(f) @@ -55,6 +64,10 @@ private[data] sealed trait IdTMonad[F[_]] extends Monad[IdT[F, ?]] { IdT(F0.tailRecM(a)(f(_).value)) } +private[data] sealed trait IdTMonad[F[_]] extends Monad[IdT[F, ?]] with IdTApplicative[F] with IdTFlatMap[F] { + implicit val F0: Monad[F] +} + private[data] sealed trait IdTFoldable[F[_]] extends Foldable[IdT[F, ?]] { implicit val F0: Foldable[F] @@ -71,46 +84,58 @@ private[data] sealed trait IdTFoldable[F[_]] extends Foldable[IdT[F, ?]] { F0.get(fa.value)(idx) } -private[data] sealed trait IdTTraverse[F[_]] extends Traverse[IdT[F, ?]] with IdTFoldable[F] { +private[data] sealed trait IdTTraverse[F[_]] extends Traverse[IdT[F, ?]] with IdTFoldable[F] with IdTFunctor[F] { implicit val F0: Traverse[F] def traverse[G[_]: Applicative, A, B](fa: IdT[F, A])(f: A => G[B]): G[IdT[F, B]] = fa.traverse(f) } -private[data] sealed abstract class IdTInstances1 { +private[data] sealed abstract class IdTInstances4 { implicit def catsDataFunctorForIdT[F[_]](implicit F: Functor[F]): Functor[IdT[F, ?]] = - new IdTFunctor[F] { - implicit val F0: Functor[F] = F - } + new IdTFunctor[F] { implicit val F0: Functor[F] = F } +} + +private[data] sealed abstract class IdTInstances3 extends IdTInstances4 { + implicit def catsDataApplyForIdT[F[_]](implicit F: Apply[F]): Apply[IdT[F, ?]] = + new IdTApply[F] { implicit val F0: Apply[F] = F } +} + +private[data] sealed abstract class IdTInstances2 extends IdTInstances3 { + implicit def catsDataApplicativeForIdT[F[_]](implicit F: Applicative[F]): Applicative[IdT[F, ?]] = + new IdTApplicative[F] { implicit val F0: Applicative[F] = F } +} + +private[data] sealed abstract class IdTInstances1 extends IdTInstances2 { + implicit def catsDataFlatMapForIdT[F[_]](implicit F: FlatMap[F]): FlatMap[IdT[F, ?]] = + new IdTFlatMap[F] { implicit val F0: FlatMap[F] = F } } private[data] sealed abstract class IdTInstances0 extends IdTInstances1 { implicit def catsDataMonadForIdT[F[_]](implicit F: Monad[F]): Monad[IdT[F, ?]] = - new IdTMonad[F] { - implicit val F0: Monad[F] = F - } + new IdTMonad[F] { implicit val F0: Monad[F] = F } implicit def catsDataFoldableForIdT[F[_]](implicit F: Foldable[F]): Foldable[IdT[F, ?]] = - new IdTFoldable[F] { - implicit val F0: Foldable[F] = F - } + new IdTFoldable[F] { implicit val F0: Foldable[F] = F } - implicit def catsDataOrderForIdT[F[_], A](implicit F: Order[F[A]]): Order[IdT[F, A]] = + implicit def catsDataEqForIdT[F[_], A](implicit F: Eq[F[A]]): Eq[IdT[F, A]] = F.on(_.value) } private[data] sealed abstract class IdTInstances extends IdTInstances0 { implicit def catsDataTraverseForIdT[F[_]](implicit F: Traverse[F]): Traverse[IdT[F, ?]] = - new IdTTraverse[F] { - implicit val F0: Traverse[F] = F - } + new IdTTraverse[F] { implicit val F0: Traverse[F] = F } - implicit def catsDataEqForIdT[F[_], A](implicit F: Eq[F[A]]): Eq[IdT[F, A]] = + implicit def catsDataOrderForIdT[F[_], A](implicit F: Order[F[A]]): Order[IdT[F, A]] = F.on(_.value) implicit def catsDataShowForIdT[F[_], A](implicit F: Show[F[A]]): Show[IdT[F, A]] = functor.Contravariant[Show].contramap(F)(_.value) + + implicit def catsDataMonadTransForIdT: MonadTrans[IdT] = + new MonadTrans[IdT] { + def liftT[M[_]: Monad, A](ma: M[A]): IdT[M, A] = IdT(ma) + } } diff --git a/tests/src/test/scala/cats/tests/IdTTests.scala b/tests/src/test/scala/cats/tests/IdTTests.scala index a748178d9fa..7b665a3bd9b 100644 --- a/tests/src/test/scala/cats/tests/IdTTests.scala +++ b/tests/src/test/scala/cats/tests/IdTTests.scala @@ -1,24 +1,86 @@ -package cats.tests +package cats +package tests -import cats.{Foldable, Functor, Monad, Traverse} import cats.data.IdT -import cats.laws.discipline.{CartesianTests, FoldableTests, FunctorTests, MonadTests, SerializableTests, TraverseTests} +import cats.kernel.laws.OrderLaws +import cats.laws.discipline._ import cats.laws.discipline.arbitrary._ class IdTTests extends CatsSuite { - implicit val iso = CartesianTests.Isomorphisms.invariant[IdT[List, ?]] + implicit val iso = CartesianTests.Isomorphisms.invariant[IdT[ListWrapper, ?]](IdT.catsDataFunctorForIdT(ListWrapper.functor)) - checkAll("IdT[Functor, Int]", FunctorTests[IdT[List, ?]].functor[Int, Int, Int]) - checkAll("Functor[IdT[List, ?]]", SerializableTests.serializable(Functor[IdT[List, ?]])) + { + implicit val F = ListWrapper.eqv[Option[Int]] - checkAll("IdT[List, Int]", MonadTests[IdT[List, ?]].monad[Int, Int, Int]) - checkAll("Monad[IdT[List, ?]]", SerializableTests.serializable(Monad[IdT[List, ?]])) + checkAll("IdT[ListWrapper, Int]", OrderLaws[IdT[ListWrapper, Int]].eqv) + checkAll("Eq[IdT[ListWrapper, Int]]", SerializableTests.serializable(Eq[IdT[ListWrapper, Int]])) + } - checkAll("IdT[Option, Int]", FoldableTests[IdT[Option, ?]].foldable[Int, Int]) - checkAll("Foldable[IdT[Option, ?]]", SerializableTests.serializable(Foldable[IdT[Option, ?]])) + { + implicit val F = ListWrapper.order[Int] - checkAll("IdT[Option, Int]", TraverseTests[IdT[Option, ?]].traverse[Int, Int, Int, Int, Option, Option]) - checkAll("Traverse[IdT[Option, ?]]", SerializableTests.serializable(Traverse[IdT[Option, ?]])) + checkAll("IdT[ListWrapper, Int]", OrderLaws[IdT[ListWrapper, Int]].order) + checkAll("Order[IdT[ListWrapper, Int]]", SerializableTests.serializable(Order[IdT[ListWrapper, Int]])) + } + + { + implicit val F = ListWrapper.functor + + checkAll("IdT[ListWrapper, Int]", FunctorTests[IdT[ListWrapper, ?]].functor[Int, Int, Int]) + checkAll("Functor[IdT[ListWrapper, ?]]", SerializableTests.serializable(Functor[IdT[ListWrapper, ?]])) + } + + { + implicit val F = ListWrapper.applyInstance + + checkAll("IdT[ListWrapper, Int]", ApplyTests[IdT[ListWrapper, ?]].apply[Int, Int, Int]) + checkAll("Apply[IdT[ListWrapper, ?]]", SerializableTests.serializable(Apply[IdT[ListWrapper, ?]])) + } + + { + implicit val F = ListWrapper.applicative + + checkAll("IdT[ListWrapper, Int]", ApplicativeTests[IdT[ListWrapper, ?]].applicative[Int, Int, Int]) + checkAll("Applicative[IdT[ListWrapper, ?]]", SerializableTests.serializable(Applicative[IdT[ListWrapper, ?]])) + } + + { + implicit val F = ListWrapper.flatMap + + checkAll("IdT[ListWrapper, Int]", FlatMapTests[IdT[ListWrapper, ?]].flatMap[Int, Int, Int]) + checkAll("FlatMap[IdT[ListWrapper, ?]]", SerializableTests.serializable(FlatMap[IdT[ListWrapper, ?]])) + } + + { + implicit val F = ListWrapper.monad + + checkAll("IdT[ListWrapper, Int]", MonadTests[IdT[ListWrapper, ?]].monad[Int, Int, Int]) + checkAll("Monad[IdT[ListWrapper, ?]]", SerializableTests.serializable(Monad[IdT[ListWrapper, ?]])) + + checkAll("IdT[ListWrapper, Int]", MonadTransTests[IdT].monadTrans[ListWrapper, Int, Int]) + checkAll("MonadTrans[IdT]", SerializableTests.serializable(MonadTrans[IdT])) + } + + { + implicit val F = ListWrapper.foldable + + checkAll("IdT[ListWrapper, Int]", FoldableTests[IdT[ListWrapper, ?]].foldable[Int, Int]) + checkAll("Foldable[IdT[ListWrapper, ?]]", SerializableTests.serializable(Foldable[IdT[ListWrapper, ?]])) + } + + { + implicit val F = ListWrapper.traverse + + checkAll("IdT[ListWrapper, Int] with Option", TraverseTests[IdT[ListWrapper, ?]].traverse[Int, Int, Int, Int, Option, Option]) + checkAll("Traverse[IdT[ListWrapper, ?]]", SerializableTests.serializable(Traverse[IdT[ListWrapper, ?]])) + } + + + test("flatMap and flatMapF consistent") { + forAll { (idT: IdT[Option, Int], f: Int => IdT[Option, Int]) => + idT.flatMap(f) should === (idT.flatMapF(f(_).value)) + } + } } diff --git a/tests/src/test/scala/cats/tests/ListWrapper.scala b/tests/src/test/scala/cats/tests/ListWrapper.scala index 7bf02bfccbd..91187fd6f81 100644 --- a/tests/src/test/scala/cats/tests/ListWrapper.scala +++ b/tests/src/test/scala/cats/tests/ListWrapper.scala @@ -97,6 +97,8 @@ object ListWrapper { val monad: Monad[ListWrapper] = monadCombine + val flatMap: FlatMap[ListWrapper] = monadCombine + val applicative: Applicative[ListWrapper] = monadCombine /** apply is taken due to ListWrapper being a case class */