scalascalazreader-monadkleisli

Composing Kleisli and Reader monad in Scala


Suppose I have functions like this:

val fooXAB: X => A => Try[B] = ...
val fooXBC: X => B => Try[C] = ...
val fooXCD: X => C => Try[D] = ...

I'd like to compose them to make a new function fooXAD: X => A => Try[D], which calls fooXAB, fooXBC, and fooXCD sequentially and pass the X argument to all of them.

Suppose I use scalaz and have a monad instance for scala.util.Try. Now I can do it this way:

type AB = Kleisli[Try, A, B]
type BC = Kleilsi[Try, B, C]
type CD = Kleisli[Try, C, D]

type XReader[T] = Reader[X, T]

val fooXAB: XReader[AB] = ...
val fooXBC: XReader[BC] = ...
val fooXCD: XReader[CD] = ...

val fooXAC: XReader[AC] =
  for {
    ab <- fooXAB
    bc <- fooXBC
    cd <- fooXCD
  } yield (ab andThen bc andThen cd)  

Does it make sense? Is it possible to simplify it ?


Solution

  • So I don't think the Reader Monad on the outer function helps here. once you apply and X to your threee XReaders, you can use Kleisli composition on the results (assuming you have a Monad instance for Try). Here's your example reworked this way, and it compiles for me:

    import scala.util.{Try,Success}
    
    import scalaz._
    import Scalaz._
    
    object A
    object B
    object C
    object D
    trait X
    
    object Main {
      implicit val pretendTryIsAMonad: Monad[Try] = new Monad[Try] {
        def point[A](a: => A): Try[A] = Success(a)
        def bind[A,B](fa: Try[A])(f: A => Try[B]): Try[B] = fa flatMap f
      }
    
      type AB = Kleisli[Try, A.type, B.type]
      type BC = Kleisli[Try, B.type, C.type]
      type CD = Kleisli[Try, C.type, D.type]
      type AD = Kleisli[Try, A.type, D.type]
    
      type XReader[T] = X => T
    
      val fooXAB: XReader[AB] = (x: X) => Kleisli((a: A.type) => Success(B))
      val fooXBC: XReader[BC] = (x: X) => Kleisli((b: B.type) => Success(C))
      val fooXCD: XReader[CD] = (x: X) => Kleisli((c: C.type) => Success(D))
    
      val fooXAD: XReader[AD] = (x: X) =>
        fooXAB(x) >=> fooXBC(x) >=> fooXCD(x)
    }