2017-04-11 29 views
4

我正在玩scalaz的monad变形金刚。我正在试图将一个Writer与底层的Id monad堆叠在一起。为了组合它们,我使用了MonadReaderMonadWriter类型类。如何在scalaz中堆叠ReaderT和WriterT变换器?

我设法编译和运行下面的代码示例而不作家(即与Reader单子,即ReaderT[Id.Id, String, A])。当添加WriterT到堆栈中,我得到的编译错误:

Gist.scala:10: could not find implicit value for parameter F: scalaz.MonadReader[Gist.R,String] 
    val MR = MonadReader[R, String] 
        ^

我怎样才能获得的MonadReader实例为我的变压器栈?我必须使用ReaderWriterStateT还是有另一种方式?

全码:

import scalaz.{Id, MonadListen, MonadReader, ReaderT, WriterT} 

object Gist { 
    import scalaz.std.list._ 
    import scalaz.syntax.monad._ 

    type P[A] = ReaderT[Id.Id, String, A] 
    type R[A] = WriterT[P, List[String], A] 

    val MR = MonadReader[R, String] 
    val MW = MonadListen[R, List[String]] 

    def apply: R[String] = MR.ask >>= { greeting => 
    MW.tell(List(s"greeting $greeting")) >>= { _ => 
     MW.point(s"Hello $greeting") 
    } 
    } 
} 

回答

5

我不完全知道为什么Scalaz不提供此实例(或类似的单子变压器MonadReader情况下),但我猜的答案有事情做与事实上,WriterTInstanceN已经goes past 11MonadReader增加只会让事情更加混乱。

你可以在Scalaz的GitHub问题中挖掘一些问题(甚至可以在IRC频道上询问是否有胃),但我不确定答案的重要性。

你可以很直接地端口从Haskell的mtl实例:

instance (Monoid w, MonadReader r m) => MonadReader r (Strict.WriterT w m) where 
    ask = lift ask 
    local = Strict.mapWriterT . local 
    reader = lift . reader 

从而转化为斯卡拉看起来是这样的:

import scalaz.{ MonadReader, MonadTrans, Monoid, WriterT } 
import scalaz.syntax.monad._ 

implicit def monadReaderForWriterT[F[_], I, W](implicit 
    F: MonadReader[F, I], 
    W: Monoid[W] 
): MonadReader[WriterT[F, W, ?], I] = new MonadReader[WriterT[F, W, ?], I] { 
    def ask: WriterT[F, W, I] = MonadTrans[WriterT[?[_], W, ?]].liftM(F.ask) 

    def local[A](f: I => I)(fa: WriterT[F, W, A]): WriterT[F, W, A] = 
    fa.mapT(F.local(f)) 

    def point[A](a: => A): WriterT[F, W, A] = a.point[WriterT[F, W, ?]] 
    def bind[A, B](fa: WriterT[F, W, A])(
    f: A => WriterT[F, W, B] 
): WriterT[F, W, B] = fa.flatMap(f) 
} 

请注意,我使用kind-projector,因为类型拉姆达版本会更像Haskell版本的四到五倍而不是三倍。

一旦你定义这种情况下,你可以写:

import scalaz.{ Id, MonadListen, ReaderT } 
import scalaz.std.list._ 

type P[A] = ReaderT[Id.Id, String, A] 
type R[A] = WriterT[P, List[String], A] 

val MR = MonadReader[R, String] 
val MW = MonadListen[R, List[String]] 

def apply: R[String] = MR.ask >>= { greeting => 
    MW.tell(List(s"greeting $greeting")) >>= { _ => 
    MW.point(s"Hello $greeting") 
    } 
} 
+0

就像一个魅力。为了教学目的,你能想到另一个没有'scalaz'实现的变压器栈/类型组合,所以我可以练习编写我自己的实现吗? – mgryszko

+0

@mgryszko你可以看到Haskell的mtl [here](https://hackage.haskell.org/package/mtl-2.2.1/docs/Control-Monad-Reader-Class.html)-'StateT'的列表, 'ListT','ContT','EitherT','IdT'等都是不错的选择。 –