Ответ 1
Если вы посмотрите на определение Reader
в источнике Scalaz, вы увидите следующее:
type Reader[-E, +A] = ReaderT[Id, E, A]
Что говорит нам о том, что монада Reader
, которую вы используете, - это просто специализация монадного трансформатора, где монада обертывается тривиальной монадой Id
. Вы можете напрямую использовать ReaderT
, но обертываете свою монаду OptionT[IO, _]
вместо того, чтобы просто обернуть все в Reader
. Например, следующее должно делать то, что вы хотите:
type OptionIO[+A] = OptionT[IO, A]
def findFfmpeg: ReaderT[OptionIO, Config, String] =
Kleisli[OptionIO, Config, String](c => findFile(c.ffmpegLocations))
def findFfmpegWrapper: ReaderT[OptionIO, Config, String] =
Kleisli[OptionIO, Config, String](c => findFile(c.ffmpegWrapperLocations))
def encode(fileName: String): ReaderT[OptionIO, Config, Unit] = (for {
w <- findFfmpegWrapper
b <- findFfmpeg
stream <- Kleisli[OptionIO, Config, Stream[String]](
_ => callFfmpeg(getCommand(w, b, fileName)).liftM[OptionT]
)
} yield stream).map(_ foreach println)
В принципе вы должны иметь возможность заменить деталь после stream <-
следующим образом:
callFfmpeg(getCommand(w, b, fileName)).liftM[OptionT].liftReaderT[Config]
Но по какой-то причине механизм Unapply
, на который полагается liftReaderT
, похоже, не работает в этом случае. К счастью, запись части Kleisli
явно не такая ужасная.
В качестве сноски: синтаксис nice liftReaderT
, о котором я упоминал, становится доступным, если вы определяете экземпляр UnapplyCo
следующим образом:
implicit def unapplyMFA1[TC[_[_]], F[+_], M0[F[+_], +_], A0](
implicit TC0: TC[({ type L[x] = M0[F, x] })#L]
): UnapplyCo[TC, M0[F, A0]] {
type M[+X] = M0[F, X]
type A = A0
} = new UnapplyCo[TC, M0[F, A0]] {
type M[+X] = M0[F, X]
type A = A0
def TC = TC0
def leibniz = Leibniz.refl
}
Я не уверен в своей голове, есть ли причина, по которой Scalaz 7 в настоящее время не предоставляет этот экземпляр, но, вероятно, стоит посмотреть.