diff --git a/core/src/main/scala/cats/data/WriterT.scala b/core/src/main/scala/cats/data/WriterT.scala index e1edf6e620..a5b5ed0d96 100644 --- a/core/src/main/scala/cats/data/WriterT.scala +++ b/core/src/main/scala/cats/data/WriterT.scala @@ -3,8 +3,12 @@ package data import cats.kernel.instances.tuple._ import cats.functor.{Bifunctor, Contravariant} +import cats.syntax.semigroup._ final case class WriterT[F[_], L, V](run: F[(L, V)]) { + def tell(l: L)(implicit functorF: Functor[F], semigroupL: Semigroup[L]): WriterT[F, L, V] = + mapWritten(_ |+| l) + def written(implicit functorF: Functor[F]): F[L] = functorF.map(run)(_._1) @@ -244,6 +248,8 @@ private[data] sealed trait WriterTMonadWriter[F[_], L] extends MonadWriter[Write def pass[A](fa: WriterT[F, L, (L => L, A)]): WriterT[F, L, A] = WriterT(F0.flatMap(fa.value) { case (f, a) => F0.map(fa.written)(l => (f(l), a)) }) + + override def tell(l: L): WriterT[F, L, Unit] = WriterT.tell(l) } private[data] sealed trait WriterTSemigroupK[F[_], L] extends SemigroupK[WriterT[F, L, ?]] { diff --git a/core/src/main/scala/cats/data/package.scala b/core/src/main/scala/cats/data/package.scala index a3cfa3edb6..f2b54b3368 100644 --- a/core/src/main/scala/cats/data/package.scala +++ b/core/src/main/scala/cats/data/package.scala @@ -39,6 +39,10 @@ package object data { type Writer[L, V] = WriterT[Id, L, V] object Writer { def apply[L, V](l: L, v: V): WriterT[Id, L, V] = WriterT[Id, L, V]((l, v)) + + def value[L:Monoid, V](v: V): Writer[L, V] = WriterT.value(v) + + def tell[L](l: L): Writer[L, Unit] = WriterT.tell(l) } type State[S, A] = StateT[Eval, S, A] diff --git a/tests/src/test/scala/cats/tests/WriterTTests.scala b/tests/src/test/scala/cats/tests/WriterTTests.scala index b3f7630403..e176cb2718 100644 --- a/tests/src/test/scala/cats/tests/WriterTTests.scala +++ b/tests/src/test/scala/cats/tests/WriterTTests.scala @@ -70,6 +70,25 @@ class WriterTTests extends CatsSuite { writerT.show should === ("(List(Some log message),foo)") } + test("tell appends to log") { + val w1: Writer[String, Int] = Writer.value(3) + val w2 = w1.tell("foo") + w2 should === (Writer("foo", 3)) + w2.tell("bar") should === (Writer("foobar", 3)) + } + + test("MonadWriter's tell is consistent with WriterT's tell") { + type Logged[A] = Writer[String, A] + val w = MonadWriter[Logged, String] + val x = w.tell("foo") + x should === (Writer.tell("foo")) + x should === (Writer("foo", ())) + } + + test("tell instantiates a Writer") { + Writer.tell("foo").written should === ("foo") + } + { // F has a SemigroupK implicit val F: SemigroupK[ListWrapper] = ListWrapper.semigroupK