我有以下代码使用Reader
monad进行配置,还必须处理IO[Option[String]]
,我最终得到的代码是encode
函数中的阶梯式步骤
如何为Reader
和OptionT
制定monad变换器,以避免我的for
函数中出现丑陋的嵌套encode
理解?
def encode(fileName: String): Reader[Config, IO[Unit]] = for {
ffmpegWrapper <- findFfmpegWrapper
ffmpegBin <- findFfmpeg
} yield (for {
w <- ffmpegWrapper
b <- ffmpegBin
stream <- callFfmpeg(getCommand(w, b, fileName)).liftM[OptionT]
} yield stream) map (_ foreach (println)) getOrElse Unit.box {}
def getCommand(ffmpegWrapper: String, ffmpegBin: String,
videoFile: String) = s"$ffmpegWrapper $ffmpegBin $videoFile '-vcodec libx264 -s 1024x576' /tmp/out.mp4"
def callFfmpeg(command: String): IO[Stream[String]] = IO {
Process(command).lines_!
}
def findFile(path:List[String]): OptionT[IO,String] = OptionT[IO,String](IO{path.find(new File(_).exists)})
def findFfmpeg:Reader[Config, OptionT[IO,String]] = Reader {c=>findFile(c.ffmpegLocations)}
def findFfmpegWrapper:Reader[Config, OptionT[IO,String]] = Reader {c=>findFile(c.ffmpegWrapperLocations)}
感谢 - 你!
答案 0 :(得分:13)
如果查看definition of Reader
in the Scalaz source,您会看到:
type Reader[-E, +A] = ReaderT[Id, E, A]
这告诉我们你正在使用的Reader
monad只是monad变换器的一个特化,其中被包裹的monad是普通的Id
monad。您可以直接使用ReaderT
,但请将OptionT[IO, _]
monad包裹起来,而不是仅将所有内容包装在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
部分并不是那么可怕,幸运的是。
作为脚注:如果你定义一个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目前是否提供此实例是否有原因,但它可能值得研究。