How to I convert between monad stacks with transformers in scalaz 7
Asked Answered
H

1

6

I'm struggling with understanding monad stacks and monad transformers with Scalaz7. I feel I'm pretty close to the answer but just can't get my head around a particular step.

The following code looks on disk for an ffmpeg binary, then creates an executable command to be run, then executes that command and then does something trivial with the output.

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

The code is kicked off by running

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

This code works, but you will notice that callFfmpeg's type signature is IO[Option[Stream[String]]] instead of IO[Stream[String]]. I had to change it to that to get it to type check, but really since all callFfmpeg does is call execute a process which returns a Stream it's type signature should be IO[Stream[String]].

My question is, given that at the time I call callFfmpeg I'm dealing with IO[Option[String]] how do I get to IO[Option[Stream[String]]]?

Hotel answered 20/5, 2013 at 21:6 Comment(1)
Why not have findFfmpeg return a plain old IO[Option[String]] action and then map into that?Antonetteantoni
H
1

So I've managed to transform the type by using liftM[OptionT].

So my callFfmpeg function can become:

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

and my getStream function now becomes:

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

This allows the conversion from IO[Stream[String]] to IO[Option[Stream[String]]] which is what I'm after.

Hotel answered 21/5, 2013 at 19:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.