1

I wish to fold a list of Writer Monad from cats to one big writer without running them. for example:

import cats.data.Writer
import cats.instances.vector._
import cats.instances.list._
import cats.instances.tuple._
import cats.Foldable

val result = for {
  i <- Writer(Vector("hello"), Vector(1))
  j <- Writer(Vector("bye"), Vector(2))
} yield j

val result2 = for {
  x <- Writer(Vector("hi"), Vector(33))
  y <- Writer(Vector("ciao"), Vector(55))
} yield y

val l = List(result, result2)

val result3 = for {
  t <- result
  z <- result2
} yield z // Success !!!

val l = List(result, result2)

// Logically:
// val result3 = for {
//   r <- l
//   o <- r
// } yield o
// But will not compile without Monad Transformer


// With run
val l1: List[(Vector[String], Vector[Int])] = l.map(_.run)

val result5 = Foldable[List].combineAll(l1)

I believe there must be a functional construct for such combination without running the Writers

Noam Shaish
  • 1,613
  • 2
  • 16
  • 37
  • What's the input and output you expect for? Are you looking for something like `List[WriterT[F,L,V]] => WriterT[F,L,V]`? – Ethan Sep 22 '20 at 21:37
  • And would the expected output of `run`ning the `WriterT` be `(Vector("hello", "bye", "hi", "ciao"), Vector(55))`? – Ethan Sep 22 '20 at 21:39

2 Answers2

1

You can skip right to result5 by using Vector so you have the same container type and then using l.sequence.map(_.flatten) Check out the Traverse typeclass, because as the saying goes "Its always Traverse".

import cats.data.{Writer, WriterT}
import cats.instances.vector._
import cats.instances.list._
import cats.instances.tuple._
import cats.{Foldable, Id}
import cats.implicits._

val result = for {
  i <- Writer(Vector("hello"), Vector(1))
  j <- Writer(Vector("bye"), Vector(2))
} yield j

val result2 = for {
  x <- Writer(Vector("hi"), Vector(33))
  y <- Writer(Vector("ciao"), Vector(55))
} yield y

val l = Vector(result, result2)

val result3 = for {
  t <- result
  z <- result2
} yield z // Success !!!

// val l = List(result, result2) -- this is a duplicate

val result5: WriterT[Id, Vector[String], Vector[Int]] = l.sequence.map(_.flatten)

result5 will have the value:

WriterT((Vector(hello, bye, hi, ciao),Vector(2, 55)))
Nigel Benns
  • 1,236
  • 11
  • 14
  • importing ```cats.implicits._ ``` cause me some issues but ```import cats.syntax.traverse._ ``` worked like magic. – Noam Shaish Sep 23 '20 at 06:42
1

Thanks to @NigelBeans answer. I will just post here the working solutions since I had some implicit conflicts using the exact imports from the answer:

import cats.data.Writer
import cats.instances.vector._
import cats.instances.list._
import cats.instances.tuple._
import cats.Foldable
import cats.syntax.traverse._

val result1 = for {
  i <- Writer(Vector("hello"), Vector(1))
  j <- Writer(Vector("bye"), Vector(2))
} yield j

val result2 = for {
  x <- Writer(Vector("hi"), Vector(33))
  y <- Writer(Vector("ciao"), Vector(55))
} yield y

val l = Vector(result1, result2)

val result3 = l.flatSequence

println(result3.run) // (Vector(hello, bye, hi, ciao),Vector(2, 55))
Noam Shaish
  • 1,613
  • 2
  • 16
  • 37