Как избежать перехода с Monad Transformers в scala?
У меня есть следующий код, который использует Reader
монада для конфигурации, а также имеет дело с IO[Option[String]]
и я закончил с кодом, который ступеньки в моем encode
функция.
Как я могу сформулировать монадный трансформатор для 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)}
Спасибо!
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
к счастью, расставаться явно не так уж и ужасно.
В качестве сноски: хороший 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 в настоящее время не предоставляет этот экземпляр, но, вероятно, стоит разобраться.