我正在尝试学习monad变换器,基于标准的Haskell库(mtl?变换器?不确定哪一个来自我的Haskell平台下载 - 7.4.1)。
我相信我注意到的是每个monad变换器定义的通用结构:
基本类型('基础')
变压器类型('BaseT')
Monad实例
MonadTrans实例
MonadIO实例
变压器类('MonadBase')
一些操作
其他'BaseT's
例如,对于Writer monad,有:
这是monad变形金刚的组织方式吗?我错过了什么/我有任何不正确的细节吗?
这个问题的动机是搞清楚:
答案 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
的用例 - 当您不知道实际的变压器类型时。
MonadTrans
是MonadWriter
中声明的类型类。它的方法(mtl
,writer
,pass
和tell
)与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)
(“显式提升”)。感谢MonadTrans
,MonadReader
开箱即用(“隐式提升”)。请阅读RWH书籍,了解其工作原理。
(在我们从两个不同模块中导入(III)
的示例中,这就是我们需要合格导入的原因。通常一次只能使用其中一个。)
此外,我并不是要特别询问
ask
。
不确定我理解...... Writer
,Reader
和其他人使用相同的架构。将State
替换为Writer
,您将获得State
的解释。