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]]]
?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With