diff --git a/core/src/main/scala/cats/data/WriterT.scala b/core/src/main/scala/cats/data/WriterT.scala index 247425ab9b..69dbc5f079 100644 --- a/core/src/main/scala/cats/data/WriterT.scala +++ b/core/src/main/scala/cats/data/WriterT.scala @@ -1,6 +1,8 @@ package cats package data +import cats.functor.Bifunctor + final case class WriterT[F[_], L, V](run: F[(L, V)]) { def written(implicit functorF: Functor[F]): F[L] = functorF.map(run)(_._1) @@ -31,6 +33,9 @@ final case class WriterT[F[_], L, V](run: F[(L, V)]) { def mapBoth[M, U](f: (L, V) => (M, U))(implicit functorF: Functor[F]): WriterT[F, M, U] = WriterT { functorF.map(run)(f.tupled) } + def bimap[M, U](f: L => M, g: V => U)(implicit functorF: Functor[F]): WriterT[F, M, U] = + mapBoth((l, v) => (f(l), g(v))) + def mapWritten[M](f: L => M)(implicit functorF: Functor[F]): WriterT[F, M, V] = mapBoth((l, v) => (f(l), v)) @@ -51,6 +56,12 @@ private[data] sealed abstract class WriterTInstances extends WriterTInstances0 { // on an algebra release that includes https://github.com/non/algebra/pull/82 implicit def writerTIdEq[L, V](implicit E: Eq[(L, V)]): Eq[WriterT[Id, L, V]] = writerTEq[Id, L, V] + + implicit def writerTBifunctor[F[_]:Functor]: Bifunctor[WriterT[F, ?, ?]] = + new Bifunctor[WriterT[F, ?, ?]] { + def bimap[A, B, C, D](fab: WriterT[F, A, B])(f: A => C, g: B => D): WriterT[F, C, D] = + fab.bimap(f, g) + } } private[data] sealed abstract class WriterTInstances0 extends WriterTInstances1 { diff --git a/tests/src/test/scala/cats/tests/WriterTTests.scala b/tests/src/test/scala/cats/tests/WriterTTests.scala index a3cb0bd18c..9bc21198f4 100644 --- a/tests/src/test/scala/cats/tests/WriterTTests.scala +++ b/tests/src/test/scala/cats/tests/WriterTTests.scala @@ -2,6 +2,7 @@ package cats package tests import cats.data.{Writer, WriterT} +import cats.functor.Bifunctor import cats.laws.discipline._ import cats.laws.discipline.eq._ import cats.laws.discipline.arbitrary._ @@ -56,6 +57,9 @@ class WriterTTests extends CatsSuite { Functor[Writer[ListWrapper[Int], ?]] Functor[Logged] + + checkAll("WriterT[ListWrapper, ?, ?]", BifunctorTests[WriterT[ListWrapper, ?, ?]].bifunctor[Int, Int, Int, Int, Int, Int]) + checkAll("Bifunctor[WriterT[ListWrapper, ?, ?]]", SerializableTests.serializable(Bifunctor[WriterT[ListWrapper, ?, ?]])) } // We have varying instances available depending on `F` and `L`.