为什么 Free 不是 Scalaz 7.1.5 中的 monad 实例?
Why Free is not monad instance in Scalaz 7.1.5?
由于 Free
不是 Scalaz 7.1.5 中的 monad 实例,我无法使用 Applicative
、Apply
等中定义的有用方法。
/* 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
错误消息是,
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
实现 monad 实例吗?
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)
}
或者,我错过了什么吗? (例如导入)
这只是 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
原样并自己定义 monad 实例,因为 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
版本的实例(Rob Norris 是 responsible对于这段代码,我刚刚对其进行了 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
}
这允许您使用 FreeC
而无需每次都定义 monad 实例。不过,我仍然认为放弃 FreeC
并使用 Free
是一个更好的主意。
由于 Free
不是 Scalaz 7.1.5 中的 monad 实例,我无法使用 Applicative
、Apply
等中定义的有用方法。
/* 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
错误消息是,
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
实现 monad 实例吗?
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)
}
或者,我错过了什么吗? (例如导入)
这只是 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
原样并自己定义 monad 实例,因为 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
版本的实例(Rob Norris 是 responsible对于这段代码,我刚刚对其进行了 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
}
这允许您使用 FreeC
而无需每次都定义 monad 实例。不过,我仍然认为放弃 FreeC
并使用 Free
是一个更好的主意。