Confusion about StateT, State and MonadState
Once upon a time, there was a State
type:
-- Not the current definition.
newtype State s a = State {runState :: s -> (a, s)}
State s a
values are, in essence, functions that take a state and produce a result and an updated state. Suitable Functor
, Applicative
and Monad
instances make it possible to compose such functions in a more convenient manner, by making the tuple shuffling need to handle the (a, s)
output implicit. With the help of a handful basic of operations that manipulate the state...
get = State $ \s -> (s, s)
put s = State $ \_ -> ((), s)
... it is possible to avoid any mention of the underlying s -> (a, s)
type, and write code that feels stateful.
StateT s
is a monad transformer patterned after State s
:
newtype StateT s m a = StateT {runStateT :: s -> m (a, s)}
This transformer adds the state processing capabilities described above atop a base monad, m
. It comes with Functor
, Applicative
and Monad
instances, as well as versions of get
and put
.
If m
, the base monad, in StateT s m
is Identity
, the dummy functor...
newtype Identity a = Identity {runIdentity :: a}
... we get something equivalent to plain old State s
. That being so, transformers defines State
as a synonym...
type State s = StateT s Identity
... rather than as a separate type.
As for MonadState
, it caters to two different needs. Firstly, we can use the monad transformers machinery to have StateT s m
as a base monad for some other transformer in a stack of transformers (arbitrary example: MaybeT (StateT Int IO)
). In that case, though, lift
from MonadTrans
becomes necessary to use get
and put
. One way of using the operations directly in such cases is through MonadState
: it provides them as methods...
-- Abridged class definition.
class Monad m => MonadState s m | m -> s where
get :: m s
put :: s -> m ()
state :: (s -> (a, s)) -> m a
... so that we can have instances for any combination of transformers involving StateT
that we are interested in.
instance Monad m => MonadState s (StateT s m) where -- etc.
instance MonadState s m => MonadState s (MaybeT m) where -- etc.
-- And so forth
Secondly, if we want to have a state monad with an implementation different than the one in transformers, we can make it an instance of MonadState
, so that we keep the same basic operations and, as long as we write type signatures in terms of MonadState
, have it easier to change implementations if need be.
State
is for your normal state monad. This is the simplest of the three. (In some older tutorials, you may see use of the State
constructor, but this has been replaced with the state
function, because State s
is now a type alias for StateT s Identity
.)
StateT
is the monad transformer for the State
monad. It adds a layer of generality by allowing you to put an arbitrary monad inside the state. This is useful for simple parsers, which could use e.g. StateT [Token] Maybe Result
to represent the parsing as a stateful operation that could fail.
MonadState
generalizes the situation even farther. There is an instance Monad m => MonadState s (StateT s m)
, but there are also instances, like one that allows you to perform stateful operations on monad transformers of StateT
. All basic state functions (get
, set
, modify
, etc.) can be used with an instance of MonadState
.