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类?
答案 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的类型相关联。 MultiParamTypeClasses
与FunctionalDependencies
是一种方式。但是,您也可以使用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采用的方法。