单子变换器的解剖学

9

我正在尝试学习Monad Transformers,基于标准的Haskell库(mtl?transformers?不确定哪一个是我下载的Haskell平台 - 7.4.1自带的)。

我认为我已经注意到每个Monad Transformer定义的共同结构:

  1. 基本类型(“Base”)

    • Monad实例
  2. 变换器类型(“BaseT”)

    • Monad实例

    • MonadTrans实例

    • MonadIO实例

  3. 变换器类(“MonadBase”)

    • 一些操作

    • 其他“BaseT”的实例

因此,例如对于Writer Monad,将会有:

  • Writer数据类型/新类型/类型,带有Monad实例
  • WriterT数据类型/新类型/类型,带有Monad,MonadTrans和MonadIO实例
  • 一个MonadWriter类,并针对StateT,ReaderT,IdentityT等的此类实例

这就是Monad Transformers的组织方式吗?我错过了什么吗/有任何不正确的细节吗?

这个问题的动机是找出:

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

1
优秀的单子和变换器资源:http://monads.haskell.cz/html/index.html - Matt Fenwick
1个回答

4

mtl 包没有实现 Monad Transformers。至少,WriterT 只是从 transformers 中重新导出的。

transformers 包实现了 WriterT,它本身就是一个 Monad Transformer。 Writer 只是一个别名:

type Writer w = WriterT w Identity

一些库可以单独实现Writer,但它只是WriterT的一种特殊情况。(Identity是一个微不足道的monad,它没有任何额外的行为。) MonadTrans允许您将底层monad包装到转换后的monad中。您可以不使用它,但您需要执行手动包装(请参见WriterTMonadTrans实例定义,以了解如何执行此操作)。唯一需要MonadTrans的用例是--当您不知道转换器的实际类型时。 MonadWriter是在mtl中声明的类型类。它的方法(writerpasstelllisten)与WriterT的函数相同。它允许通过转换器堆栈自动包装WriterT计算,即使您不知道堆栈中转换器的确切类型(甚至数量!)。
因此,WriterT是唯一“必需”的类型。
对于其他monad转换器也是一样:BaseT是一个转换器,Base是一个没有底层monad的monad,而MonadBase是一个类型类--所有具有BaseT的monad都属于该类。 添加: 您可以在RWH书籍中找到很好的解释。
以下是一个基本示例:
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)(尝试编译以查看错误信息!)。但是,由于MonadTrans(“显式提升”),(II)可以编译。由于MonadReader(III)可以直接使用(“隐式提升”)。请阅读 RWH 书了解其工作原理。
(在本例中,我们从两个不同的模块导入ask,这就是为什么我们需要限定导入的原因。通常您一次只会使用其中一个。)
引用:
我并不是特别想问关于Writer的问题。
不确定我是否理解...ReaderState和其他内容都使用相同的架构。将Writer替换为State,您将获得有关State的说明。

2
谢谢回复!然而,我认为对于那些对单子变换器知之甚少的人(比如我 :)),答案可以通过一些额外的清晰度和细节来改进。也许添加一些示例会有所帮助;例如,演示MonadTransMonadBase之间的区别。此外,我并不是特别询问Writer。话虽如此,我并不是要对这篇文章持消极态度--它很有帮助,也很受欢迎。谢谢! - Matt Fenwick
2
嗯,还不太明白。我有点理解你写的内容,但如果我要自己实现一个Transformer,我不知道从哪里开始——我不知道关键概念是什么,它们如何相互关联。看来需要付出一些努力! - Matt Fenwick
一个单子变换器只是单子范畴中指向函子。你有什么问题? - winitzki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接