2015-11-19 27 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

相關問題