Monad变压器的解剖学

时间:2012-11-28 16:19:56

标签: haskell monads monad-transformers

我正在尝试学习monad变换器,基于标准的Haskell库(mtl?变换器?不确定哪一个来自我的Haskell平台下载 - 7.4.1)。

我相信我注意到的是每个monad变换器定义的通用结构:

  1. 基本类型('基础')

    • Monad实例
  2. 变压器类型('BaseT')

    • Monad实例

    • MonadTrans实例

    • MonadIO实例

  3. 变压器类('MonadBase')

    • 一些操作

    • 其他'BaseT's

    • 的实例
  4. 例如,对于Writer monad,有:

    • 一个Writer数据类型/ newtype / type,带有Monad实例
    • 一个WriterT数据类型/ newtype / type,包含Monad,MonadTrans和MonadIO实例
    • 一个MonadWriter类,以及StateT,ReaderT,IdentityT等的这个类的实例......

    这是monad变形金刚的组织方式吗?我错过了什么/我有任何不正确的细节吗?

    这个问题的动机是搞清楚:

    1. “BaseT”与相应的“MonadBase”和“Base”之间的关系和差异是什么
    2. 是否都需要这三个
    3. MonadTrans如何相关以及它的目的是什么

1 个答案:

答案 0 :(得分:4)

mtl包不实现monad变换器。至少WriterT只是来自transformers的{​​{3}}。

transformers包实现WriterT,这是monad转换器本身。 Writer只是一个别名:

type Writer w = WriterT w Identity

有些图书馆可以单独实施Writer,但无论如何它只是WriterT的一个特例。 (Identity是一个琐碎的monad,它没有任何其他行为。)

MonadTrans允许您将基础monad包装到已转换的monad中。你可以没有它,但你需要进行手动包装(参见MonadTrans的{​​{1}}实例定义,例如如何做)。唯一需要WriterT的用例 - 当您不知道实际的变压器类型时。

MonadTransMonadWriter中声明的类型类。它的方法(mtlwriterpasstell)与listen的函数相同。它允许通过堆栈的变换器包装(自动!)WriterT计算,即使你不知道堆栈中变换器的确切类型(甚至是数字!)。

因此,WriterT是唯一的“必需”类型。

对于其他monad变换器,它是相同的:WriterT是变换器,BaseT是没有底层monad的monad,Base是类型类 - 所有monad的类,在变形金刚堆栈的某处有MonadBase

<强>增加:

您可以在reexported

中找到很好的解释

这是一个基本的例子:

BaseT

请注意,import Control.Monad.Trans import Control.Monad.Trans.Writer import Control.Monad.Trans.Reader hiding (ask) -- `ask` from transformers -- ask :: Monad m => ReaderT r m r import qualified Control.Monad.Trans.Reader as TransReader (ask) -- `ask` from mtl -- ask :: MonadReader r m => m r import qualified Control.Monad.Reader as MtlReader (ask) -- Our monad transformer stack: -- It supports reading Int and writing String type M m a = WriterT String (ReaderT Int m) a -- Run our monad runM :: Monad m => Int -> M m a -> m (a, String) runM i action = runReaderT (runWriterT action) i test :: Monad m => M m Int test = do tell "hello" -- v <- TransReader.ask -- (I) will not compile v1 <- lift TransReader.ask -- (II) ok v2 <- MtlReader.ask -- (III) ok return (v1 + v2) main :: IO () main = runM 123 test >>= print 将被编译器拒绝(尝试查看错误消息!)。但(I)编译,感谢(II)(“显式提升”)。感谢MonadTransMonadReader开箱即用(“隐式提升”)。请阅读RWH书籍,了解其工作原理。

(在我们从两个不同模块中导入(III)的示例中,这就是我们需要合格导入的原因。通常一次只能使用其中一个。)

  

此外,我并不是要特别询问ask

不确定我理解...... WriterReader和其他人使用相同的架构。将State替换为Writer,您将获得State的解释。