如何在 scalaz 中自定义 monad 转换器
How to customize monad transformer in scalaz
我最近在学习 scalaz。当我将 scalaz 与 Haskell 进行比较时,我发现很难自定义我自己的 monad 转换器。
我可以在 Haskell 中执行的代码如下:
newtype Box a = Box a deriving Show
-- Monad instance of Box, it's simple, I pass it..
newtype BoxT m a = BoxT {runBoxT :: m (Box a)}
instance (Monad m) => Monad (BoxT m) where
return a = BoxT $ return $ Box a
x >>= f = BoxT $ do
(Box v) <- runBoxT x
runBoxT $ f v
instance (Monad m) => Applicative (BoxT m) where
pure = return
(<*>) = ap
instance (Monad m) => Functor (BoxT m) where
fmap = liftM
如何在 scalaz 中做同样的事情?我试过很多方法,但看起来我无法弄清楚scala的泛型类型,像这样:
case class Box[A](value:A)
case class BoxT[M[_],A](run: M[Box[A]])
implicit val monad = Monad[BoxT] { // goes wrong, type `BoxT[M[_],A]` doesn't match F[_]
... //don't know how to override functions..
}
感谢@Iva Kam 对投影仪的评论。
首先,在build.sbt中启用kind projector:https://github.com/typelevel/kind-projector
addCompilerPlugin("org.typelevel" %% "kind-projector" % "0.11.0" cross CrossVersion.full)
那么代码可以这样工作:
case class Box[A](value:A)
case class BoxT[M[_],A](run: M[Box[A]])
implicit def monad[M[_]](implicit m : Monad[M]): Monad[BoxT[M, ?]] = new Monad[BoxT[M,?]] {
override def point[A](a: => A): BoxT[M,A] = BoxT(m.point(Box(a)))
override def bind[A, B](fa: BoxT[M,A])(f: A => BoxT[M,B]): BoxT[M,B] = BoxT(m.bind(fa.run){ b: Box[A] =>
val v = b.value
f(v).run
})
}
def main(args: Array[String]): Unit = {
val a = Monad[BoxT[Option,?]].point(2)
val b = for {
v <- a //v should be a Int, but idea can't analyze it, maybe I should update, still in 2018.2
} yield {v * 4}
val c = a >> b //this line works, but idea can't analyze it :(
println(b)
println(c)
}
但看起来当我使用 BoxT 的 Monad 时,我应该使用 ?
而不是 Box 的内部类型 A
。
我最近在学习 scalaz。当我将 scalaz 与 Haskell 进行比较时,我发现很难自定义我自己的 monad 转换器。
我可以在 Haskell 中执行的代码如下:
newtype Box a = Box a deriving Show
-- Monad instance of Box, it's simple, I pass it..
newtype BoxT m a = BoxT {runBoxT :: m (Box a)}
instance (Monad m) => Monad (BoxT m) where
return a = BoxT $ return $ Box a
x >>= f = BoxT $ do
(Box v) <- runBoxT x
runBoxT $ f v
instance (Monad m) => Applicative (BoxT m) where
pure = return
(<*>) = ap
instance (Monad m) => Functor (BoxT m) where
fmap = liftM
如何在 scalaz 中做同样的事情?我试过很多方法,但看起来我无法弄清楚scala的泛型类型,像这样:
case class Box[A](value:A)
case class BoxT[M[_],A](run: M[Box[A]])
implicit val monad = Monad[BoxT] { // goes wrong, type `BoxT[M[_],A]` doesn't match F[_]
... //don't know how to override functions..
}
感谢@Iva Kam 对投影仪的评论。
首先,在build.sbt中启用kind projector:https://github.com/typelevel/kind-projector
addCompilerPlugin("org.typelevel" %% "kind-projector" % "0.11.0" cross CrossVersion.full)
那么代码可以这样工作:
case class Box[A](value:A)
case class BoxT[M[_],A](run: M[Box[A]])
implicit def monad[M[_]](implicit m : Monad[M]): Monad[BoxT[M, ?]] = new Monad[BoxT[M,?]] {
override def point[A](a: => A): BoxT[M,A] = BoxT(m.point(Box(a)))
override def bind[A, B](fa: BoxT[M,A])(f: A => BoxT[M,B]): BoxT[M,B] = BoxT(m.bind(fa.run){ b: Box[A] =>
val v = b.value
f(v).run
})
}
def main(args: Array[String]): Unit = {
val a = Monad[BoxT[Option,?]].point(2)
val b = for {
v <- a //v should be a Int, but idea can't analyze it, maybe I should update, still in 2018.2
} yield {v * 4}
val c = a >> b //this line works, but idea can't analyze it :(
println(b)
println(c)
}
但看起来当我使用 BoxT 的 Monad 时,我应该使用 ?
而不是 Box 的内部类型 A
。