cats-mtl icon indicating copy to clipboard operation
cats-mtl copied to clipboard

FunctorListen doesn't understand nested Monad stack

Open thobson opened this issue 5 years ago • 0 comments

I'm trying to stack a WriterT and EitherT together - ultimately something like IO[Writer[Log, Either[Throwable, A]]]. The idea is that even in the event of an error the log up to that point should be maintained.

I have created a POC which uses a transformer stack of EitherT[WriterT[IO, Log, *], Throwable, *] and when I materialise the program it works as expected. The logs are present even in the event of an error. However FunctorListen seems to short circuit in the event of an error. Here is the code itself:

object POC extends IOApp {

  import cats.instances.vector._
  import cats.mtl.implicits._
  import cats.syntax.flatMap._
  import cats.syntax.functor._

  type Log = Vector[String]

  override def run(args: List[String]): IO[ExitCode] = {
    val name = getName[EitherT[WriterT[IO, Log, *], Throwable, *]]
    // this works even with a raised error so the stack itself is working
    name.value.written.flatTap(logs => IO(println(logs))).as(ExitCode.Success)
    // this won't work
    printLog(name).value.run.as(ExitCode.Success)
  }

  def printLog[F[_], A](fa: F[A])(implicit F: Sync[F], FL: FunctorListen[F, Log]): F[A] = {
    // this is never called when an error is raised
    fa.listen.flatMap { case (a, logs) => F.delay(println(logs)).as(a) }
  }

  def getName[F[_]](implicit F: Sync[F], FT: FunctorTell[F, Log], FR: FunctorRaise[F, Throwable]): F[String] = for {
    _ <- FT.tell(Vector("getting name ..."))
    a <- F.delay("bob")
    // Exception here means FunctorListen cant find any logs
    _ <- FR.raise(new Exception("boom!")): F[Unit]
  } yield a

}

I'm not sure if this is a problem with FunctorListen or FunctorRaise. It could be that FunctorRaise is raising an error at the IO level not the Either/EitherT

thobson avatar Jan 13 '20 09:01 thobson