5
class Monad m => MonadState s m | m -> s where
    -- | Return the state from the internals of the monad.
    get :: m s
    get = state (\s -> (s, s))

    -- | Replace the state inside the monad.
    put :: s -> m ()
    put s = state (\_ -> ((), s))

    -- | Embed a simple state action into the monad.
    state :: (s -> (a, s)) -> m a
    state f = do
      s <- get
      let ~(a, s') = f s
      put s'
      return a

instance MonadState s m => MonadState s (MaybeT m) where...

为什么一个 MonadState 的实例需要一个 state 和一个 monad,为什么不创建一个单参数的 State 类呢?

4

2 回答 2

6

让我试着在评论中回答 Gert 的问题,因为这是一个完全不同的问题。

问题是,为什么我们不能只写

class State s where
   get :: s
   put :: s -> ()

好吧,我们可以写这个。但现在的问题是,我们能用它做什么?困难的部分是,如果我们有一些代码,put x然后稍后get,我们如何将 链接getput以便返回与输入的值相同的值?

问题是,只有类型()and s,没有办法将一个链接到另一个。您可以尝试以各种方式实现它,但它不会起作用。没有办法将数据从put传送到get(也许有人可以更好地解释这一点,但最好的理解方法是尝试编写它)。

Monad 不一定是使操作可链接的唯一方法,但它是一种方法,因为它具有将>>两个语句链接在一起的运算符:

(>>) :: m a -> m b -> m b

所以我们可以写

(put x) >> get

编辑:这是一个使用包中定义的实例的示例StateT

foo :: StateT Int IO ()
foo = do
    put 3
    x <- get
    lift $ print x

main = evalStateT foo 0
于 2012-09-02T18:19:23.263 回答
4

您需要某种方式将状态的类型与 monad 的类型相关联。MultiParamTypeClasseswithFunctionalDependencies是一种方法。但是,您也可以使用TypeFamilies.

class (Monad m) => MonadState m where
    type StateType m

    -- | Return the state from the internals of the monad.
    get :: m (StateType m)
    get = state (\s -> (s, s))

    -- | Replace the state inside the monad.
    put :: StateType m -> m ()
    put s = state (\_ -> ((), s))

    -- | Embed a simple state action into the monad.
    state :: (StateType m -> (a, StateType m)) -> m a
    state f = do
      s <- get
      let ~(a, s') = f s
      put s'
      return a

instance MonadState m => MonadState (MaybeT m) where
    type StateType (MaybeT m) = StateType m

    ...

这是monads-tf 包采用的方法。

于 2012-09-02T17:09:30.480 回答