Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why Free is not monad instance in Scalaz 7.1.5?

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)

like image 770
1ambda Avatar asked Nov 19 '15 00:11

1ambda


1 Answers

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.

like image 55
Travis Brown Avatar answered Nov 18 '22 06:11

Travis Brown