我有以下代码使用 Reader
monad用于配置并且还必须处理 IO[Option[String]]
我最终得到的代码是我的 encode
功能。
我怎样才能制作一个monad变换器 Reader
和 OptionT
避免丑陋的嵌套 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)}
谢谢!
如果你看看 的定义 Reader
在Scalaz源代码中,你会看到这个:
type Reader[-E, +A] = ReaderT[Id, E, A]
哪个告诉我们了 Reader
你正在使用的monad只是一个monad变换器的特化,其中monad被包裹是微不足道的 Id
单子。您可以使用 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目前是否提供此实例是否有原因,但它可能值得研究。
如果你看看 的定义 Reader
在Scalaz源代码中,你会看到这个:
type Reader[-E, +A] = ReaderT[Id, E, A]
哪个告诉我们了 Reader
你正在使用的monad只是一个monad变换器的特化,其中monad被包裹是微不足道的 Id
单子。您可以使用 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目前是否提供此实例是否有原因,但它可能值得研究。