关于 StateT、State 和 MonadState 的困惑

Confusion about StateT, State and MonadState

我一头雾水

newtype StateT s m a = StateT {runStateT :: s -> m (a, s)}

type State s = StateT s Identity

class Monad m => MonadState s m | m -> s

State 适用于您的正常状态 monad。这是三者中最简单的。 (在一些较早的教程中,您可能会看到 State 构造函数的使用,但这已被 state 函数取代,因为 State s 现在是 StateT s Identity 的类型别名.)

StateTState monad 的 monad 转换器。它通过允许您在状态中放置任意 monad 来增加一层通用性。这对于简单的解析器很有用,它可以使用例如StateT [Token] Maybe Result 将解析表示为可能会失败的有状态操作。

MonadState 进一步概括了这种情况。有一个实例 Monad m => MonadState s (StateT s m),但也有一些实例,例如允许您对 StateT 的 monad 转换器执行有状态操作的实例。所有基本状态函数(getsetmodify 等)都可以与 MonadState.

的实例一起使用

从前,有一个State类型:

-- Not the current definition.
newtype State s a = State {runState :: s -> (a, s)}

State s a 值本质上是获取状态并产生结果和更新状态的函数。合适的 FunctorApplicativeMonad 实例使得以更方便的方式组合此类函数成为可能,方法是使元组改组需要隐式处理 (a, s) 输出。借助一些操纵状态的基本操作...

get = State $ \s -> (s, s)
put s = State $ \_ -> ((), s)

...可以避免提及底层 s -> (a, s) 类型,并编写 感觉 有状态的代码。

StateT s 是在 State s:

之后模式化的 monad 转换器
newtype StateT s m a = StateT {runStateT :: s -> m (a, s)}

此转换器在基础 monad 上添加了上述状态处理功能,m。它带有 FunctorApplicativeMonad 实例,以及 getput.

的版本

如果 m,基础 monad,在 StateT s m 中是 Identity,虚拟函子...

newtype Identity a = Identity {runIdentity :: a}

...我们得到了相当于普通旧 State s 的东西。既然如此,transformersState 定义为同义词...

type State s = StateT s Identity

...而不是作为一个单独的类型。

至于MonadState,它迎合了两种不同的需求。首先,我们可以使用 monad transformers 机制将 StateT s m 作为 transformer 堆栈中其他一些 transformer 的基础 monad(任意示例:MaybeT (StateT Int IO))。但是,在那种情况下,MonadTrans 中的 lift 变得有必要使用 getput。在这种情况下直接使用操作的一种方法是通过 MonadState:它以方法的形式提供它们...

-- Abridged class definition.
class Monad m => MonadState s m | m -> s where
    get :: m s
    put :: s -> m ()
    state :: (s -> (a, s)) -> m a

... 这样我们就可以拥有我们感兴趣的涉及 StateT 的任意变换器组合的实例。

instance Monad m => MonadState s (StateT s m) where -- etc.
instance MonadState s m => MonadState s (MaybeT m) where -- etc.
-- And so forth

其次,如果我们想要一个状态 monad,其实现不同于 transformers,我们可以将其作为 MonadState 的一个实例,这样我们保持相同的基本操作,并且只要我们根据 MonadState 编写类型签名,就可以在需要时更轻松地更改实现。