How let State works with Kleisli?

131 Views Asked by At

I'm having an example for a logger wrapped with State monad:

    val logger = Logger(LoggerFactory.getLogger(this.getClass))

    def logState[A](s:IO[Unit], a:A): State[List[IO[Unit]], A] = State[List[IO[Unit]], A]{ logs =>
        (logs :+ s, a)
    }

    type Valid[A] = Exception \/ A

    def i2f(i:Int): Valid[BigDecimal] = if (i >= 0) BigDecimal(i).right else (new RuntimeException("Input is smaller then 0")).left
    def f2s(f: Valid[BigDecimal]): Valid[String] = f match {
        case \/-(f1) => f1.toString.right
        case -\/(e) => e.left
    }

    val comp: Int => State[List[IO[Unit]], Valid[String]] = i => for{
        f <- logState(IO{ logger.info(s" => i2f($i)")}, i2f(i))
        s <- logState(IO{ logger.info(s" => f2s($f)")}, f2s(f))
    } yield s

    comp(2)(List.empty) match {
        case (logs, a) => {
            logs.foreach(_.unsafePerformIO())
            a match {
                case \/-(s) => println(s"Finally we get: ${s}")
                case -\/(e) => println(e.getMessage)
            }
        }
    }

Which works well, but I'm not satisfy with as before I adding State monad, the code was much more clear which was:

    type Valid[A] = Exception \/ A

    def i2f: Kleisli[Valid, Int, BigDecimal] = Kleisli { i =>
        if (i >= 0) BigDecimal(i).right else (new RuntimeException("Input is smaller then 0")).left
    }

    def f2s: Kleisli[Valid, BigDecimal, String] = Kleisli { f =>
        f.toString().right
    }

    def comp: Kleisli[Valid, Int, String] = i2f andThen f2s

    comp(2) match {
        case \/-(s) => println(s"Finally we get: ${s}")
        case -\/(e) => println(e.getMessage)
    }

I'm wondering how let State to work with Kleisli? so that all monads will be working together likes one?

And not the logger will works out of i2f and f2s functions, but also are able to work inside?

1

There are 1 best solutions below

0
On

All right, got some progress, now the code been:

    implicit val ec =  scala.concurrent.ExecutionContext.global

    type Valid[A] = Exception \/ A
    type Report = List[IO[Unit]]
    type StateResultT[A] = StateT[Future, Report, A]

    implicit val StateResultBind: Bind[StateResultT] = new Bind[StateResultT] {
        override def bind[A, B](fa: StateResultT[A])(f: A => StateResultT[B]): StateResultT[B] = fa flatMap f
        override def map[A, B](fa: StateResultT[A])(f: A => B): StateResultT[B] = fa map f
    }

    def i2f: Kleisli[StateResultT, Int, Valid[BigDecimal]] = Kleisli{ i =>
        StateT { logs =>
            Future (
                    logs :+ IO(logger.debug("i2f")),
                    if (i >= 0) BigDecimal(i).right else (new RuntimeException("Input is smaller then 0")).left
            )
        }
    }

    def f2s: Kleisli[StateResultT, Valid[BigDecimal], (Report, Valid[String])] = Kleisli { s =>
        StateT { logs =>
            Future (
                logs :+ IO(logger.debug("f2s")),
                s match{
                    case \/-(f) => f.toString.right
                    case -\/(e) => e.left
                }
            )
        }
    }

    def comp: Kleisli[StateResultT, Int, Valid[String]] = i2f andThen f2s

    Await.result(comp(-2)(List.empty), Duration.Inf) match {
        case (logs, a) => {
            logs.foreach(_.unsafePerformIO())
            a match {
                case \/-(s) => println(s"Finally we get: ${s}")
                case -\/(e) => println(e.getMessage)
            }
        }
    }