Вот небольшая композиция функций, каждая из которых возвращает 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))
})
Как реализовать fc в случае, если f1 вернет Reader, но не ReaderT:
def f1:Reader[Map[String,String], Boolean] = Reader(_ => true)
Теперь мне нужно составить Reader, а именно ReaderT[Id, ...] с Reader[FailFast, ...]





Как вы упомянули, Reader[A, B] — это просто ReaderT[Id, A, B] (что само по себе является псевдонимом типа Kleisli[Id, A, B]).
Поскольку вы используете кошек, существует метод mapK, который сопоставляет первый параметр типа ReaderT, вам просто нужно предоставить экземпляр FunctionK/~> для преобразования. Итак, в вашем случае это будет выглядеть примерно так:
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))
})
Вероятно, есть некоторые другие рефакторинги, которые могли бы еще больше очистить его, например, использование EitherT, но, поскольку это выглядит немного надуманным примером, я оставлю это в качестве упражнения для читателя.
Спасибо. Я пытался использовать этот подход, но с некоторыми ошибками использовал mapF вместо mapK, и без вспомогательной функции Id2FailFast не мог преобразовать его соответствующим образом. Единственное изменение, которое мне пришлось внести в ваше решение, — изменить тип f с Id на Id[T]. Без этого я получил ошибку: тип Id принимает параметры типа def apply[T](f: Id): FailFast[T] = Right(f)