2013-06-12 73 views
11

我有以下代碼使用Reader monad進行配置,並且還必須處理IO[Option[String]],並且我已經使用我的encode函數中的階梯代碼結束了代碼。如何避免scala中使用Monad變形金剛階梯式步進?

我怎樣才能制定一個單子轉換爲ReaderOptionT避免我encode功能醜陋的嵌套for內涵?

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)} 

謝謝!

回答

13

如果你看一下definition of Reader in the Scalaz source,你會看到這一點:

type Reader[-E, +A] = ReaderT[Id, E, A] 

它告訴我們您所使用的Reader單子只是一個單子轉換,其中被包裹的單子的專業化是微不足道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的部分並不是那麼可怕。


作爲一個註腳:

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 
} 

我不知道把我的頭頂部是否有一個原因:如果你定義一個UnapplyCo例如像這樣漂亮的liftReaderT語法我提到可用Scalaz 7目前不提供這個實例,但它可能值得研究。

+1

當你出現時,我正在寫一個幾乎相同的答案。我修改了你的答案的頂部,這些東西是在我的,但沒有在你的顯示Reader = ReaderT類型別名,請隨時刪除它,如果你認爲它不會添加到您的答案:) – stew

+0

@stew:謝謝!我剛剛添加了一個鏈接到您提到的來源。 –

+0

特拉維斯和@stew這非常有幫助!現在就試試吧。 – cwmyers