10

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

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

  1. 基本类型(“基本”)

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

    • 单子实例

    • MonadTrans 实例

    • MonadIO 实例

  3. 变压器类('MonadBase')

    • 一些操作

    • 其他“BaseT”的实例

例如,对于 Writer monad,会有:

  • 带有 Monad 实例的 Writer 数据类型/新类型/类型
  • 一个 WriterT 数据类型/新类型/类型,带有 Monad、MonadTrans 和 MonadIO 实例
  • 一个 MonadWriter 类,以及 StateT、ReaderT、IdentityT 的此类的实例...

这是monad转换器的组织方式吗?我遗漏了什么/我有任何不正确的细节吗?

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

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

1 回答 1

4

mtl包没有实现 monad 转换器。至少 WriterT只是transformers.

transformerspackage implements WriterT,它本身就是一个 monad 转换器。Writer只是一个别名:

type Writer w = WriterT w Identity

有些库可以Writer单独实现,但无论如何它只是WriterT. (Identity是一个微不足道的单子,它没有任何额外的行为。)

MonadTrans允许您将底层 monad 包装到转换后的 monad 中。您可以没有它,但您需要执行手动包装(请参阅MonadTrans实例定义以WriterT了解如何执行此操作)。您真正需要的唯一用例MonadTrans——当您不知道变压器的实际类型时。

MonadWriter是在中声明的类型类mtl。它的方法(writerpass和)telllisten函数 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

不确定我理解... ReaderState其他人使用相同的架构。替换WriterState,您将对State.

于 2012-11-28T17:40:12.003 回答