Reputation: 9505
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, ...]
Upvotes: 0
Views: 1070
Reputation: 18663
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.
Upvotes: 2