Customising composition of Future, Either and Writer in Scalaz

丶灬走出姿态 提交于 2019-12-01 01:06:37

This article: Composing monadic effects explains the issue.

So...

type MyMonad e w a = ErrorT e (Writer w) a is isomorphic to (Either e a, w)

type MyMonad e w a = WriterT w (Either e) a is isomorphic to Either r (a, w)

Reordering the stack of monad transformers as follows solves the problem:

import scalaz._, Scalaz._

class Example[F[_], L] (val logFn: (String) => L)(implicit val f: Monad[F], l: Monoid[L])
{
  type T = Throwable
  type WF[α] = WriterT[F, L, α]
  type EWF[α] = EitherT[WF, T, α]

  private def unreliableInt (i: Int): T Either Int = {
    new java.util.Random ().nextBoolean match {
      case false => Right (i)
      case true => Left (new Exception (":-("))
    }
  }

  private def fn (i: Int): EWF[Int] = unreliableInt (i) match {
    case Left (left) => EitherT.left [WF, T, Int] (WriterT.put[F, L, T] (f.point (left))(l.zero))
    case Right (right) => EitherT.right [WF, T, Int] (WriterT.put[F, L, Int] (f.point (right))(l.zero))
  }

  private def log (msg: String): EWF[Unit] = { EitherT.right[WF, T, Unit](WriterT.put[F, L, Unit] (f.point (()))(logFn (msg))) }

  private def foo (): EWF[Int] = for {
    a <- log ("Start")
    x <- fn (18)
    b <- log ("Middle")
    y <- fn (42)
    c <- log ("End")
  } yield x + y

  def bar (): F[(Option[Int], L)] = {
    val barEWF: EWF[Int] = foo ()

    // Pull out the logs.
    val logsF: F[L] = barEWF.run.written

    // Pull out the value.
    val resF: F[Option[Int]] = barEWF.run.value.map {
      case \/- (r) => r.some
      case -\/ (ex) => None
    }

    for {
      logs <- logsF
      response <- resF
    } yield (response, logs)
  }
}

object Program
{
  def main (args : Array[String]) = {
    import scala.concurrent._
    import scala.concurrent.duration._
    import ExecutionContext.Implicits.global

    type L = List[String]
    type F[α] = Future[α]

    implicit val l: Monoid[L] = new Monoid[L] { def zero = Nil; def append (f1: L, f2: => L) = f1 ::: f2 }
    implicit val f: Monad[F] = scalaz.std.scalaFuture.futureInstance

    def createLog (s: String) = s :: Nil
    val example = new Example[F, L] (createLog)
    val result = Await.result (example.bar (), 5 seconds)
    println ("Context logs attached:" + result._2.foldLeft ("") { (a, x) => a + "\n$ " + s"$x"})
    println ("Result:" + result._1)
  }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!