Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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]]]?

like image 708
cwmyers Avatar asked May 20 '13 21:05

cwmyers


1 Answers

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.

like image 161
cwmyers Avatar answered Oct 21 '22 05:10

cwmyers