Since Free
is not a monad instance in Scalaz 7.1.5, I can't use useful method defined in Applicative
, Apply
and so on.
/* ref - http://tpolecat.github.io/assets/sbtb-slides.pdf */
import Free._, Coyoneda._
type ResultSetIO[A] = FreeC[ResultSetOp, A]
val next : ResultSetIO[Boolean] = liftFC(Next)
def getString(index: Int): ResultSetIO[String] = liftFC(GetString(index))
def getInt(index: Int) : ResultSetIO[Int] = liftFC(GetInt(index))
def close : ResultSetIO[Unit] = liftFC(Close)
// compile errors
def getPerson1: ResultSetIO[Person] =
(getString(1) |@| getInt(2)) { Person(_, _)}
def getNextPerson: ResultSetIO[Person] =
next *> getPerson
def getPeople(n: Int): ResultSetIO[List[Person]] =
getNextPerson.replicateM(n) // List.fill(n)(getNextPerson).sequence
the erorr message is,
Error:(88, 19) value |@| is not a member of free.JDBC.ResultSetIO[String]
(getString(1) |@| getInt(2)) { Person(_, _)}
^
Error:(91, 10) value *> is not a member of free.JDBC.ResultSetIO[Boolean]
next *> getPerson
^
Error:(94, 19) value replicateM is not a member of free.JDBC.ResultSetIO[free.Person]
getNextPerson.replicateM(n) // List.fill(n)(getNextPerson).sequence
^
Should I implement monad instance for Free
?
implicit val resultSetIOMonadInstance = new Monad[ResultSetIO] {
override def bind[A, B](fa: ResultSetIO[A])(f: (A) => ResultSetIO[B]): ResultSetIO[B] =
fa.flatMap(f)
override def point[A](a: => A): ResultSetIO[A] =
Free.point[CoyonedaF[ResultSetOp]#A, A](a)
}
Or, am I missing something? (e.g import)
This is just the Scala compiler being fussy about type aliases. You have two choices (or at least two choices—there are probably other reasonable workarounds). The first is to break down the type alias slightly differently. Instead of this:
type ResultSetIO[A] = FreeC[ResultSetOp, A]
You write this:
type CoyonedaResultSetOp[A] = Coyoneda[ResultSetOp, A]
type ResultSetIO[A] = Free[CoyonedaResultSetOp, A]
And then Monad[ResultSetIO]
will compile just fine. You will need one extra import for |@|
, *>
, and replicateM
:
import scalaz.syntax.applicative._
The other option is to leave the FreeC
as it is and define the monad instance yourself, since scalac won't find it for you. Fortunately you can do this a little more simply than writing it out as you propose:
implicit val monadResultSetIO: Monad[ResultSetIO] =
Free.freeMonad[({ type L[x] = Coyoneda[ResultSetOp, x] })#L]
I prefer the first approach, but it doesn't really matter which you choose.
Here's a simplified complete working example for the sake of convenience:
sealed trait ResultSetOp[A]
case object Next extends ResultSetOp[Boolean]
case class GetString(index: Int) extends ResultSetOp[String]
case class GetInt(index: Int) extends ResultSetOp[Int]
case object Close extends ResultSetOp[Unit]
import scalaz.{ Free, Coyoneda, Monad }
import scalaz.syntax.applicative._
type CoyonedaResultSetOp[A] = Coyoneda[ResultSetOp, A]
type ResultSetIO[A] = Free[CoyonedaResultSetOp, A]
val next: ResultSetIO[Boolean] = Free.liftFC(Next)
def getString(index: Int): ResultSetIO[String] = Free.liftFC(GetString(index))
def getInt(index: Int): ResultSetIO[Int] = Free.liftFC(GetInt(index))
def close: ResultSetIO[Unit] = Free.liftFC(Close)
case class Person(s: String, i: Int)
def getPerson: ResultSetIO[Person] = (getString(1) |@| getInt(2))(Person(_, _))
def getNextPerson: ResultSetIO[Person] = next *> getPerson
def getPeople(n: Int): ResultSetIO[List[Person]] = getNextPerson.replicateM(n)
This will compile just fine with 7.1.5.
For the sake of completeness, there's a third way, which is to define some Unapply
machinery to help the compiler find instances for the FreeC
version (Rob Norris is responsible for this code, which I've just de-kind-projected):
implicit def freeMonadC[FT[_[_], _], F[_]](implicit
ev: Functor[({ type L[x] = FT[F, x] })#L]
) = Free.freeMonad[({ type L[x] = FT[F, x] })#L]
implicit def unapplyMMFA[TC[_[_]], M0[_[_], _], M1[_[_], _], F0[_], A0](implicit
TC0: TC[({ type L[x] = M0[({ type L[x] = M1[F0, x] })#L, x] })#L]
): Unapply[TC, M0[({ type L[x] = M1[F0, x] })#L, A0]] {
type M[X] = M0[({ type L[x] = M1[F0, x] })#L, X]
type A = A0
} = new Unapply[TC, M0[({ type L[x] = M1[F0, x] })#L, A0]] {
type M[X] = M0[({ type L[x] = M1[F0, x] })#L, X]
type A = A0
def TC = TC0
def leibniz = Leibniz.refl
}
This allows you to use FreeC
without defining monad instances every time. I still think just giving up on FreeC
and using Free
is a better idea, though.
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