Reputation: 676
In the "FP in Scala" book there's this approach for using an ADT S
as an abstract instruction set like
sealed trait Console[_]
case class PrintLine(msg: String) extends Console[Unit]
case object ReadLine extends Console[String]
and composing them with a Free[S, A]
where S
would later be translated to an IO monad.
Can this be done with Scalaz's Free
type? It seems that all run
methods require a functor instance for S
.
Upvotes: 3
Views: 867
Reputation: 11366
yes, you need a functor, but you can create one using Coyoneda
.
Coyoneda
will turn any F[A]
into a Coyoneda[F,A]
and Coyoneda[F,A]
is a functor.
scala> type ConsoleCoyo[A] = Coyoneda[Console, A]
defined type alias ConsoleCoyo
Then scalaz's Free has a type alias just for this:
/** A free monad over the free functor generated by `S` */
type FreeC[S[_], A] = Free[({type f[x] = Coyoneda[S, x]})#f, A]
So now We have a Free monad for console:
scala> type ConsoleMonad[A] = Free.FreeC[ConsoleCoyo,A]
defined type alias ConsoleMonad
also you'll find handy that scalaz's Free has a function to lift a F[A] directly into a monad:
/** A free monad over a free functor of `S`. */
def liftFC[S[_], A](s: S[A]): FreeC[S, A] =
liftFU(Coyoneda lift s)
so, for example:
scala> Free.liftFC(ReadLine)
res1: scalaz.Free.FreeC[Console,String] = Suspend(scalaz.Coyoneda$$anon$22@360bb132)
Upvotes: 9