mtl
包没有实现 monad 转换器。至少 WriterT只是从transformers
.
transformers
package implements WriterT
,它本身就是一个 monad 转换器。Writer
只是一个别名:
type Writer w = WriterT w Identity
有些库可以Writer
单独实现,但无论如何它只是WriterT
. (Identity
是一个微不足道的单子,它没有任何额外的行为。)
MonadTrans
允许您将底层 monad 包装到转换后的 monad 中。您可以没有它,但您需要执行手动包装(请参阅MonadTrans
实例定义以WriterT
了解如何执行此操作)。您真正需要的唯一用例MonadTrans
——当您不知道变压器的实际类型时。
MonadWriter
是在中声明的类型类mtl
。它的方法(writer
、pass
和)tell
与listen
函数 for 相同WriterT
。它允许WriterT
通过转换器堆栈(自动!)包装计算,即使您不知道堆栈中转换器的确切类型(甚至数量!)。
因此,WriterT
是唯一“必需”的类型。
对于其他 monad 转换器,它是相同的:BaseT
是一个转换器,Base
是一个没有底层 monad 的 monad,并且MonadBase
是一个类型类——所有 monad 的类,BaseT
在转换器堆栈中的某个位置。
添加:
您可以在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)
将被编译器拒绝(尝试查看错误消息!)。但是(II)
由于MonadTrans
(“显式提升”)而编译。感谢MonadReader
,(III)
开箱即用(“隐式提升”)。请阅读 RWH 书以了解其工作原理。
(在示例中,我们ask
从两个不同的模块导入,这就是我们需要限定导入的原因。通常您一次只会使用其中一个。)
另外我也不是特意问的Writer
。
不确定我理解... Reader
,State
其他人使用相同的架构。替换Writer
为State
,您将对State
.