Scala-cats, compose Reader with ReaderT

930 Views Asked by At

Here is a small composition of functions, all of which return ReaderT:

  type FailFast[A] = Either[List[String], A]

  def f1:ReaderT[FailFast, Map[String,String], Boolean] = ReaderT(_ => Right(true))
  def f2:ReaderT[FailFast, Map[String,String], Boolean] = ReaderT(_ => Left(List("d")))
  def f3:ReaderT[FailFast, Map[String,String], Boolean] = ReaderT(_ => Right(true))
  def f4:ReaderT[FailFast, Map[String,String], Boolean] = ReaderT(_ => Right(true))

  def fc:ReaderT[FailFast, Map[String,String], Boolean] =
    f1.flatMap( b1 => {
      if (b1)
        for {
          b2 <- f2
          b3 <- f3
          b4 <- f4
        } yield b4
      else ReaderT(_ => Right(true))
    })

How to implement fc in case f1 would return Reader, but not ReaderT:

def f1:Reader[Map[String,String], Boolean] = Reader(_ => true)

Now I have to compose Reader, which is exactly ReaderT[Id, ...] with Reader[FailFast, ...]

1

There are 1 best solutions below

1
On BEST ANSWER

As you mentioned Reader[A, B] is just ReaderT[Id, A, B] (which is itself just an type alias of Kleisli[Id, A, B]).

Since you are using cats there is a method called mapK which maps over the first type parameter of ReaderT, you just need to provide a FunctionK/~> instance for the conversion. So in your case it would look something like this:

val Id2FailFast = new (Id ~> FailFast) {
  def apply[T](f: Id[T]): FailFast[T] = Right(f) 
}

f1.mapK(Id2FailFast).flatMap( b1 => {
  if (b1)
    for {
      b2 <- f2
      b3 <- f3
      b4 <- f4
    } yield b4
  else ReaderT(_ => Right(true))
})

There are likely some other refactorings that could clean it up further, like using an EitherT but since it seems like a bit of a contrived example I'll leave that as an exercise for the reader.