为什么在MonadState中使用MultiParamTypeClasses

时间:2012-09-02 16:41:41

标签: haskell

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的实例需要状态和monad,为什么不创建单个参数State类?

2 个答案:

答案 0 :(得分:6)

让我试着在评论中回答格特的问题,因为这是一个非常不同的问题。

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

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

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

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

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

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

所以我们可以写

(put x) >> get

编辑:以下是使用the StateT instance defined in the package

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

main = evalStateT foo 0

答案 1 :(得分:4)

您需要某种方式将状态类型与monad的类型相关联。 MultiParamTypeClassesFunctionalDependencies是一种方式。但是,您也可以使用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 package采用的方法。