为什么有些类型类的前缀是 "Monad"?

Why are some typeclasses prefixed with "Monad"?

阅读一些代码时,我有时会遇到以 Monad 为前缀的类型类,例如 MonadStateMonadIOMonadReader

这些的具体用途是什么?

MonadState为例,我知道

但是 MonadState 究竟允许什么?

我不明白是否需要另一组同名的 types/typeclasses,有人可以解释一下吗?

MonadState 不是 monad,而是 Monad 的子类。 State 是一个单子(也是一个 MonadState)。 monad 子类 MonadState 用于使 getput 等函数在具有 gettable/settable "state".[=17 概念的任何 monad 上工作=]

StateStateT和其他类似的类型来自transformers, while the MonadState typeclass and other typeclasses like it come from mtl。注意前者是 types,而后者是 typeclasses.

transformers 中的类型是 MonadMonadTrans 的实例。您可以直接与他们合作,但有一些烦恼:

  • 如果你有一个 monad 堆栈深几层,你必须在代码中大量调用 lift 才能访问每一层的功能。

  • 有时两种不同的类型提供相同的 "interface"。例如,RWSTReaderT 都提供类似 reader 的功能,例如 ask。在编写一个函数时,不得不提交另一个函数是很烦人的,因为它降低了通用性。

来自 mtlMonad* 类型类缓解了这些问题:

  • 他们有 "pass-through" 个实例,可以消除对 lift 的许多调用(或者更准确地说,自动处理它们)。例如,StateTMonadState 的实例,但是 StateT 上的 ReaderT 也是 MonadState 的实例,因此您可以使用 get直接。

    import Control.Monad
    import Control.Monad.Reader
    import Control.Monad.State
    
    -- put the environment in the state
    bar :: ReaderT Int (State Int) ()
    bar = ask >>= lift . put -- we use lift here
    
    barMTL :: ReaderT Int (State Int) ()
    -- This ONLY works if we have imported the 
    -- required instances from mtl.
    -- The MonadState instance for ReaderT, in particular.
    barMTL = ask >>= put -- the put is auto-lifted
    
  • 您可以在您的函数中放置一个 Monad* 约束并针对 Monad* "interface" 而不是立即提交 monad 的特定实现。这样你的函数就会变得更通用,并且选择确切的 "implementation" 会延迟到最后可能的时刻。

    import Control.Monad
    import Control.Monad.State
    import Control.Monad.RWS
    
    -- Dumb function that increments the state.
    -- Doesn't commit to a specific implementation of the monad.
    baz :: MonadState Int m => m ()
    baz = modify succ
    
    main :: IO ()
    main = do
        -- run as State
        print $ runState baz 0 
        -- run as RWS
        print $ runRWS (baz >> tell ()) () 0