If you look at the definition of Reader
in the Scalaz source, you'll see this:
type Reader[-E, +A] = ReaderT[Id, E, A]
Which tells us that the Reader
monad you are using is just a specialization of a monad transformer where the monad being wrapped is the trivial Id
monad. You can use ReaderT
directly, but wrapping your OptionT[IO, _]
monad instead of just wrapping everything in a Reader
. For example, the following should do what you want:
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)
In principle you should be able to replace the part after stream <-
with the following:
callFfmpeg(getCommand(w, b, fileName)).liftM[OptionT].liftReaderT[Config]
But for some reason the Unapply
machinery that liftReaderT
relies on doesn't seem to be working in this case. Writing the Kleisli
part out explicitly isn't all that horrible, fortunately.
As a footnote: the nice liftReaderT
syntax I mentioned becomes available if you define an UnapplyCo
instance like this:
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
}
I'm not sure off the top of my head whether there's a reason Scalaz 7 doesn't currently provide this instance, but it's probably worth looking into.