2015-11-19 22 views
5

由于Free不是斯卡拉兹7.1.5中的monad实例,因此我无法使用Applicative,Apply等中定义的有用方法。为什么Free在Scalaz 7.1.5中不是monad实例?

/* 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 

的erorr消息是,

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 
      ^

我应该为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) 
} 

或者我错过了什么? (例如导入)

回答

6

这只是Scala编译器关于类型别名的烦恼。你有两种选择(或者至少有两种选择 - 可能有其他合理的解决方法)。首先是略微不同地分解类型别名。取而代之的是:

type ResultSetIO[A] = FreeC[ResultSetOp, A] 

您这样写:

type CoyonedaResultSetOp[A] = Coyoneda[ResultSetOp, A] 
type ResultSetIO[A] = Free[CoyonedaResultSetOp, A] 

然后Monad[ResultSetIO]将编译就好了。您将需要一个额外的进口为|@|*>,并replicateM

import scalaz.syntax.applicative._ 

另一种选择就是离开FreeC,因为它是和定义单子实例自己,因为scalac不会找到它。幸运的是,你可以做到这一点更简单地比写出来,你建议:

implicit val monadResultSetIO: Monad[ResultSetIO] = 
    Free.freeMonad[({ type L[x] = Coyoneda[ResultSetOp, x] })#L] 

我更喜欢第一种方法,但它事并没有真正的你选择哪一个。

这里是为了方便起见简化的完整工作示例:

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) 

这将编译只是7.1.5罚款。


为了完整起见,还有第三种方式,这是定义一些Unapply机械,以帮助编译器找到了FreeC版本的实例(罗布·诺里斯是responsible这个代码,我刚刚去-kind投影):

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 
} 

这可以让你使用FreeC没有定义每次单子实例。不过,我仍然认为放弃FreeC并使用Free是一个更好的主意。

+1

哦,还有一个:4. Upvote SI-5075,交叉手指,再等待四到五年... –

+0

感谢您提供详细的解释和其他选择。自从Scalaz 7.2.x没有'FreeC'后,我想选择第一种方法。 – 1ambda

相关问题