2015-11-19 13 views
5

Poiché Free non è un'istanza monade Scalaz 7.1.5, non posso usare metodo utile definita Applicative, Apply e così via.Perché free non è monade esempio nel Scalaz 7.1.5?

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

il messaggio 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 
      ^

Devo implementare esempio monade per 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) 
} 

Oppure, mi manca qualcosa? (Per esempio l'importazione)

risposta

6

Questo è solo il compilatore Scala essere pignoli sul tipo di alias. Avete due scelte (o almeno due scelte - probabilmente ci sono altri metodi alternativi ragionevoli). Il primo è quello di scomporre l'alias di tipo leggermente diverso. Invece di questo:

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

Lei scrive questo:

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

E poi Monad[ResultSetIO] compilerà bene. Avrete bisogno di uno di importazione extra per |@|, *>, e replicateM:

import scalaz.syntax.applicative._ 

L'altra opzione è quella di lasciare la FreeC così com'è e definire l'istanza Monade da soli, in quanto scalac non lo troverà per voi. Per fortuna si può fare questo un po 'più semplice che scrivere fuori come si propongono:

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

io preferisco il primo approccio, ma in realtà non importa che si sceglie.

Ecco un esempio di lavoro completo semplificata per comodità:

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) 

Questo compilerà bene con 7.1.5.


Per ragioni di completezza, c'è una terza via, che è quello di definire alcuni macchinari Unapply per aiutare il compilatore trovare istanze per la versione FreeC (Rob Norris è responsible per questo codice, che ho appena de -kind-proiettata):

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 
} 

Questo consente di utilizzare FreeC senza definire le istanze monade ogni volta. Continuo a pensare che solo rinunciando a FreeC e utilizzando Free è un'idea migliore, però.

+1

Oh, e uno più: 4. Upvote SI-5075, incrocia le dita e attendere altri quattro o cinque anni ... –

+0

Grazie per la spiegazione dettagliata e utili altre alternative. Vorrei scegliere il primo modo poiché Scalaz 7.2.x non ha 'FreeC'. – 1ambda