From b40cc923d6d6823f9959c8b70958442ee19a7432 Mon Sep 17 00:00:00 2001 From: Vikraman Choudhury Date: Tue, 27 Oct 2015 18:46:32 -0400 Subject: [PATCH 1/2] Add IdT, the identity monad transformer --- core/src/main/scala/cats/data/IdT.scala | 107 ++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 core/src/main/scala/cats/data/IdT.scala diff --git a/core/src/main/scala/cats/data/IdT.scala b/core/src/main/scala/cats/data/IdT.scala new file mode 100644 index 0000000000..bed9c19440 --- /dev/null +++ b/core/src/main/scala/cats/data/IdT.scala @@ -0,0 +1,107 @@ +package cats +package data + +/** + * `IdT[F[_], A]` is the identity monad transformer. + */ +final case class IdT[F[_], A](value: F[A]) { + + def map[B](f: A => B)(implicit F: Functor[F]): IdT[F, B] = + IdT(F.map(value)(f)) + + def flatMap[B](f: A => IdT[F, B])(implicit F: FlatMap[F]): IdT[F, B] = + IdT(F.flatMap(value)(f.andThen(_.value))) + + def flatMapF[B](f: A => F[B])(implicit F: FlatMap[F]): IdT[F, B] = + IdT(F.flatMap(value)(f)) + + def foldLeft[B](b: B)(f: (B, A) => B)(implicit F: Foldable[F]): B = + F.foldLeft(value, b)(f) + + def foldRight[B](lb: Eval[B])(f: (A, Eval[B]) => Eval[B])(implicit F: Foldable[F]): Eval[B] = + F.foldRight(value, lb)(f) + + def traverse[G[_], B](f: A => G[B])(implicit F: Traverse[F], G: Applicative[G]): G[IdT[F, B]] = + G.map(F.traverse(value)(f))(IdT(_)) + + def ap[B](f: IdT[F, A => B])(implicit F: Apply[F]): IdT[F, B] = + IdT(F.ap(value)(f.value)) + +} + +object IdT extends IdTInstances { + + def pure[F[_], A](a: A)(implicit F: Applicative[F]): IdT[F, A] = + IdT(F.pure(a)) +} + +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] = + fa.map(f) +} + +private[data] sealed trait IdTMonad[F[_]] extends Monad[IdT[F, ?]] { + implicit val F0: Monad[F] + + def pure[A](a: A): IdT[F, A] = + IdT.pure(a) + + def flatMap[A, B](fa: IdT[F, A])(f: A => IdT[F, B]): IdT[F, B] = + fa.flatMap(f) +} + +private[data] sealed trait IdTFoldable[F[_]] extends Foldable[IdT[F, ?]] { + implicit val F0: Foldable[F] + + def foldLeft[A, B](fa: IdT[F, A], b: B)(f: (B, A) => B): B = + fa.foldLeft(b)(f) + + def foldRight[A, B](fa: IdT[F, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = + fa.foldRight(lb)(f) +} + +private[data] sealed trait IdTTraverse[F[_]] extends Traverse[IdT[F, ?]] with IdTFoldable[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 { + implicit def idTFunctor[F[_]](implicit F: Functor[F]): Functor[IdT[F, ?]] = + new IdTFunctor[F] { + implicit val F0: Functor[F] = F + } +} + +private[data] sealed abstract class IdTInstances0 extends IdTInstances1 { + + implicit def idTMonad[F[_]](implicit F: Monad[F]): Monad[IdT[F, ?]] = + new IdTMonad[F] { + implicit val F0: Monad[F] = F + } + + implicit def idTFoldable[F[_]](implicit F: Foldable[F]): Foldable[IdT[F, ?]] = + new IdTFoldable[F] { + implicit val F0: Foldable[F] = F + } + + implicit def idTOrder[F[_], A](implicit F: Order[F[A]]): Order[IdT[F, A]] = + F.on(_.value) +} + +private[data] sealed abstract class IdTInstances extends IdTInstances0 { + + implicit def idTTraverse[F[_]](implicit F: Traverse[F]): Traverse[IdT[F, ?]] = + new IdTTraverse[F] { + implicit val F0: Traverse[F] = F + } + + implicit def idTEq[F[_], A](implicit F: Eq[F[A]]): Eq[IdT[F, A]] = + F.on(_.value) + + implicit def idTShow[F[_], A](implicit F: Show[F[A]]): Show[IdT[F, A]] = + functor.Contravariant[Show].contramap(F)(_.value) +} From 17a94ca435de326fb47ec35ea1efa851fef0c5ab Mon Sep 17 00:00:00 2001 From: Vikraman Choudhury Date: Tue, 24 Nov 2015 04:26:21 -0500 Subject: [PATCH 2/2] Add tests for IdT --- .../cats/laws/discipline/Arbitrary.scala | 3 +++ .../src/test/scala/cats/tests/IdTTests.scala | 22 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 tests/src/test/scala/cats/tests/IdTTests.scala diff --git a/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala b/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala index ee71f8d6aa..a5cabdb8df 100644 --- a/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala +++ b/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala @@ -38,6 +38,9 @@ object arbitrary extends ArbitraryInstances0 { implicit def optionTArbitrary[F[_], A](implicit F: Arbitrary[F[Option[A]]]): Arbitrary[OptionT[F, A]] = Arbitrary(F.arbitrary.map(OptionT.apply)) + implicit def idTArbitrary[F[_], A](implicit F: Arbitrary[F[A]]): Arbitrary[IdT[F, A]] = + Arbitrary(F.arbitrary.map(IdT.apply)) + implicit def evalArbitrary[A: Arbitrary]: Arbitrary[Eval[A]] = Arbitrary(Gen.oneOf( getArbitrary[A].map(Eval.now(_)), diff --git a/tests/src/test/scala/cats/tests/IdTTests.scala b/tests/src/test/scala/cats/tests/IdTTests.scala new file mode 100644 index 0000000000..393bf66f15 --- /dev/null +++ b/tests/src/test/scala/cats/tests/IdTTests.scala @@ -0,0 +1,22 @@ +package cats.tests + +import cats.{Foldable, Functor, Monad, Traverse} +import cats.data.IdT +import cats.laws.discipline.{FoldableTests, FunctorTests, MonadTests, SerializableTests, TraverseTests} +import cats.laws.discipline.arbitrary._ + +class IdTTests extends CatsSuite { + + checkAll("IdT[Functor, Int]", FunctorTests[IdT[List, ?]].functor[Int, Int, Int]) + checkAll("Functor[IdT[List, ?]]", SerializableTests.serializable(Functor[IdT[List, ?]])) + + checkAll("IdT[List, Int]", MonadTests[IdT[List, ?]].monad[Int, Int, Int]) + checkAll("Monad[IdT[List, ?]]", SerializableTests.serializable(Monad[IdT[List, ?]])) + + checkAll("IdT[Option, Int]", FoldableTests[IdT[Option, ?]].foldable[Int, Int]) + checkAll("Foldable[IdT[Option, ?]]", SerializableTests.serializable(Foldable[IdT[Option, ?]])) + + checkAll("IdT[Option, Int]", TraverseTests[IdT[Option, ?]].traverse[Int, Int, Int, Int, Option, Option]) + checkAll("Traverse[IdT[Option, ?]]", SerializableTests.serializable(Traverse[IdT[Option, ?]])) + +}