diff --git a/core/src/main/scala/cats/data/WriterT.scala b/core/src/main/scala/cats/data/WriterT.scala index 2ee07cc5cd..2c89f43dba 100644 --- a/core/src/main/scala/cats/data/WriterT.scala +++ b/core/src/main/scala/cats/data/WriterT.scala @@ -87,9 +87,6 @@ private[data] sealed abstract class WriterTInstances0 extends WriterTInstances1 implicit val L0: Monoid[L] = L } - implicit def writerTIdFunctor[L]: Functor[WriterT[Id, L, ?]] = - writerTFunctor[Id, L] - implicit def writerTIdFlatMap[L:Semigroup]: FlatMap[WriterT[Id, L, ?]] = writerTFlatMap[Id, L] @@ -111,7 +108,11 @@ private[data] sealed abstract class WriterTInstances1 extends WriterTInstances2 new WriterTMonoid[F, L, V] { implicit val F0: Monoid[F[(L, V)]] = W } + + implicit def writerTIdCoflatMap[L]: CoflatMap[WriterT[Id, L, ?]] = + writerTCoflatMap[Id, L] } + private[data] sealed abstract class WriterTInstances2 extends WriterTInstances3 { implicit def writerTMonadWriter[F[_], L](implicit F: Monad[F], L: Monoid[L]): MonadWriter[WriterT[F, L, ?], L] = new WriterTMonadWriter[F, L] { @@ -131,6 +132,7 @@ private[data] sealed abstract class WriterTInstances3 extends WriterTInstances4 implicit val F0: Alternative[F] = F implicit val L0: Monoid[L] = L } + } private[data] sealed abstract class WriterTInstances4 extends WriterTInstances5 { @@ -160,6 +162,7 @@ private[data] sealed abstract class WriterTInstances5 extends WriterTInstances6 } private[data] sealed abstract class WriterTInstances6 extends WriterTInstances7 { + implicit def writerTApply[F[_], L](implicit F: Apply[F], L: Semigroup[L]): Apply[WriterT[F, L, ?]] = new WriterTApply[F, L] { implicit val F0: Apply[F] = F @@ -168,8 +171,10 @@ private[data] sealed abstract class WriterTInstances6 extends WriterTInstances7 } private[data] sealed abstract class WriterTInstances7 { - implicit def writerTFunctor[F[_], L](implicit F: Functor[F]): Functor[WriterT[F, L, ?]] = new WriterTFunctor[F, L] { - implicit val F0: Functor[F] = F + + implicit def writerTCoflatMap[F[_], L](implicit F: Functor[F]): CoflatMap[WriterT[F, L, ?]] = + new WriterTCoflatMap[F, L] { + implicit val F0: Functor[F] = F } } @@ -259,12 +264,18 @@ private[data] sealed trait WriterTSemigroup[F[_], L, A] extends Semigroup[Writer WriterT(F0.combine(x.run, y.run)) } -private[data] sealed trait WriterTMonoid[F[_], L, A] extends Monoid[WriterT[F, L, A]] with WriterTSemigroup[F, L, A]{ +private[data] sealed trait WriterTMonoid[F[_], L, A] extends Monoid[WriterT[F, L, A]] with WriterTSemigroup[F, L, A] { override implicit def F0: Monoid[F[(L, A)]] def empty: WriterT[F, L, A] = WriterT(F0.empty) } +private[data] sealed trait WriterTCoflatMap[F[_], L] extends CoflatMap[WriterT[F, L, ?]] with WriterTFunctor[F, L] { + + def coflatMap[A, B](fa: WriterT[F, L, A])(f: WriterT[F, L, A] => B): WriterT[F, L, B] = fa.map(_ => f(fa)) +} + + trait WriterTFunctions { def putT[F[_], L, V](vf: F[V])(l: L)(implicit functorF: Functor[F]): WriterT[F, L, V] = WriterT(functorF.map(vf)(v => (l, v))) @@ -281,6 +292,3 @@ trait WriterTFunctions { def valueT[F[_], L, V](vf: F[V])(implicit functorF: Functor[F], monoidL: Monoid[L]): WriterT[F, L, V] = WriterT.putT[F, L, V](vf)(monoidL.empty) } - - - diff --git a/laws/src/main/scala/cats/laws/discipline/CoflatMapTests.scala b/laws/src/main/scala/cats/laws/discipline/CoflatMapTests.scala index 0abeff6ad9..887a2cb9d1 100644 --- a/laws/src/main/scala/cats/laws/discipline/CoflatMapTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/CoflatMapTests.scala @@ -7,18 +7,25 @@ import org.scalacheck.Prop import Prop._ import org.typelevel.discipline.Laws -trait CoflatMapTests[F[_]] extends Laws { +trait CoflatMapTests[F[_]] extends Laws with FunctorTests[F] { def laws: CoflatMapLaws[F] def coflatMap[A: Arbitrary, B: Arbitrary, C: Arbitrary](implicit ArbFA: Arbitrary[F[A]], EqFA: Eq[F[A]], - EqFC: Eq[F[C]] + EqFC: Eq[F[C]], + EqFFA: Eq[F[F[A]]], + EqFB: Eq[F[B]], + EqFFFA: Eq[F[F[F[A]]]] ): RuleSet = { new DefaultRuleSet( name = "coflatMap", - parent = None, - "coflatMap associativity" -> forAll(laws.coflatMapAssociativity[A, B, C] _)) + parent = Some(functor[A, B, C]), + "coflatMap associativity" -> forAll(laws.coflatMapAssociativity[A, B, C] _), + "coflatMap identity" -> forAll(laws.coflatMapIdentity[A, B] _), + "coflatten coherence" -> forAll(laws.coflattenCoherence[A, B] _), + "coflatten throughMap" -> forAll(laws.coflattenThroughMap[A] _) + ) } } diff --git a/tests/src/test/scala/cats/tests/WriterTTests.scala b/tests/src/test/scala/cats/tests/WriterTTests.scala index f6223e879f..55bff93d00 100644 --- a/tests/src/test/scala/cats/tests/WriterTTests.scala +++ b/tests/src/test/scala/cats/tests/WriterTTests.scala @@ -101,7 +101,7 @@ class WriterTTests extends CatsSuite { checkAll("Bifunctor[WriterT[ListWrapper, ?, ?]]", SerializableTests.serializable(Bifunctor[WriterT[ListWrapper, ?, ?]])) } - implicit val iso = CartesianTests.Isomorphisms.invariant[WriterT[ListWrapper, ListWrapper[Int], ?]](WriterT.writerTFunctor(ListWrapper.functor)) + implicit val iso = CartesianTests.Isomorphisms.invariant[WriterT[ListWrapper, ListWrapper[Int], ?]](WriterT.writerTCoflatMap(ListWrapper.functor)) // We have varying instances available depending on `F` and `L`. // We also battle some inference issues with `Id`. @@ -268,4 +268,18 @@ class WriterTTests extends CatsSuite { Semigroup[WriterT[Id, Int, Int]] } + + { + // F has a Functor + implicit val F: Functor[ListWrapper] = ListWrapper.functor + + Functor[WriterT[ListWrapper, Int, ?]] + CoflatMap[WriterT[ListWrapper, Int, ?]] + checkAll("WriterT[Listwrapper, Int, ?]", CoflatMapTests[WriterT[ListWrapper, Int, ?]].coflatMap[Int, Int, Int]) + checkAll("WriterT[ListWrapper, Int, ?]", SerializableTests.serializable(CoflatMap[WriterT[ListWrapper, Int, ?]])) + + // Id has a Functor + Functor[WriterT[Id, Int, ?]] + CoflatMap[WriterT[Id, Int, ?]] + } }