首页 文章

如何在scalaz 7中使用变换器在monad堆栈之间进行转换

提问于
浏览
6

我正在努力用 Scalaz7 来理解monad堆栈和monad变换器 . 我觉得我能绕过一个特定的步骤 .

以下代码在磁盘上查找 ffmpeg 二进制文件,然后创建要运行的可执行命令,然后执行该命令,然后对输出执行一些微不足道的操作 .

object Encoder {

  def findFfmpeg: OptionT[IO, String] = {
    OptionT[IO, String](IO {
      List("/usr/local/bin/ffmpeg", "/usr/bin/ffmpeg").find {
        new File(_).exists
      }
    }
    )
  }

  def getCommand(ffmpegBin: String,
                 videoFile: String) = s"$ffmpegBin $videoFile  '-vcodec libx264 -s 1024x576' /tmp/out.mp4"

  def callFfmpeg(command: String): IO[Option[Stream[String]]] = IO {
    Some(Process(command).lines_!)
  }

  def getStream(fileName: String): OptionT[IO, Stream[String]] = {
    val optionalCommand: OptionT[IO, String] = findFfmpeg map {
      getCommand(_, fileName)
    }
    optionalCommand >>= {
      command => OptionT[IO, Stream[String]](callFfmpeg(command))
    }
  }

  def encode(fileName: String): IO[Unit] = {
    getStream(fileName) map {
      a: Stream[String] =>
        a map {
          _.length
        } foreach (println)

    } getOrElse (Unit.box {println("nothing")})
  }
}

代码通过运行启动

Encoder.encode("/path/to/video.mp4").unsafePerformIO

此代码有效,但您会注意到 callFfmpeg 的类型签名是 IO[Option[Stream[String]]] 而不是 IO[Stream[String]] . 我不得不改变它以使其进行类型检查,但实际上因为所有 callFfmpeg 都是调用执行一个返回 Stream 的进程,它的类型签名应该是 IO[Stream[String]] .

我的问题是,鉴于当时我打电话 callFfmpeg 我正在处理 IO[Option[String]] 我怎么去 IO[Option[Stream[String]]]

1 回答

  • 1

    所以我设法通过使用 liftM[OptionT] 来转换类型 .

    所以我的 callFfmpeg 函数可以变成:

    def callFfmpeg(command: String): IO[Stream[String]] = IO {
        Process(command).lines_!
    }
    

    我的 getStream 函数现在变为:

    def getStream(fileName: String): OptionT[IO, Stream[String]] = {
        val optionalCommand: OptionT[IO, String] = findFfmpeg map {
            getCommand(_, fileName)
        }
        optionalCommand >>= {
            command => callFfmpeg(command).liftM[OptionT]
        }
    }
    

    这允许从 IO[Stream[String]]IO[Option[Stream[String]]] 的转换,这就是我所追求的 .

相关问题