Skip to content

Commit

Permalink
Merge pull request #588 from vikraman/idT
Browse files Browse the repository at this point in the history
Add IdT, the identity monad transformer
  • Loading branch information
julien-truffaut committed May 26, 2016
2 parents 011fc52 + 17a94ca commit 236199e
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 0 deletions.
107 changes: 107 additions & 0 deletions core/src/main/scala/cats/data/IdT.scala
Original file line number Diff line number Diff line change
@@ -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)
}
3 changes: 3 additions & 0 deletions laws/src/main/scala/cats/laws/discipline/Arbitrary.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,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(_)),
Expand Down
22 changes: 22 additions & 0 deletions tests/src/test/scala/cats/tests/IdTTests.scala
Original file line number Diff line number Diff line change
@@ -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, ?]]))

}

0 comments on commit 236199e

Please sign in to comment.