I want to create a graph structure using an IntMap of nodes and unique keys. This topic has been covered well here and here. I understand how the state monad works by basically wrapping a function of state -> (val,state) in a newtype so we can create a monad instance for it. I have read about the topic quite a bit. I still cant seem to get my head around how I can get unique (or just incremental) values throughout the execution of my program. It's easy enough to get a run of successive IDs, but once I "runState" to get out of the monad it seems like I'm back where I started with having to keep track of the current ID. I feel like I'm stuck in the monad. The other option I considered was to keep the entire IntMap and current "next" ID as the state, but that seems very "imperative" and extreme. This question is very similar but did not get many answers(or maybe I'm just missing something obvious). What is the idiomatic way to utilize the state monad to get unique ID throughout a program execution? Thanks.
-
Actually the more I think about it, the idea of encapsulating the entire map in a monad sounds appealing. I would like to write functions that use the ID (or map key) like the actual value, instead of having to "lookup" constantly. But, it's also late and I work with C++ all day so maybe I'm not thinking clearly. – MFlamer Jan 25 '13 at 07:36
-
I think the real question is why you feel you have to do something "outside" of `runState`. The only situation where you couldn't just push `runState` outwards instead is when you want access to a shadowed "outer" monad (say, IO). At that point you need transformers, as Gabriel suggests. – Peter Wortmann Jan 25 '13 at 17:11
-
@Peter, Yes it seems this is the real issue. – MFlamer Jan 26 '13 at 08:28
1 Answers
Let's imagine we were to IO
-ify the State
monad. What would that look like? Our pure State
monad is just a newtype around:
s -> (a, s)
Well, the IO
version might do a little bit of side effects before returning the final values, which would look like:
s -> IO (a, s)
That pattern is so common it has a name, specifically StateT
:
newtype StateT s m a = StateT { runStateT :: s -> m (a, s) }
The name has a T
at the end because it is a monad T
ransformer. We call m
the "base monad" and StateT s m
the "transformed" monad.
StateT s m
is only a Monad
if m
is a Monad
:
instance (Monad m) => Monad (StateT s m) where {- great exercise -}
However, in addition to that, all monad transformers implement the MonadTrans
class, defined as follows:
class MonadTrans t where
lift :: (Monad m) => m a -> t m a
instance MonadTrans (StateT s) where {- great exercise -}
If t
is StateT s
, then lift
's type specializes to:
lift :: m a -> StateT s m a
In other words, it lets us "lift" an action in the base monad to become an action in the transformed monad.
So, for your specific problem, you want the StateT (IntMap k v) IO
monad, which extends IO
with additional State
. Then you can write your entire program in this monad:
main = flip runStateT (initialState :: IntMap k v) $ do
m <- get -- retrieve the map
lift $ print m -- lift an IO action
(k, v) <- lift readLn
put (insert k v m)
Notice that I still use get
and put
. That's because the transformers
package implements all the concepts I described and it generalizes the signatures of get
and put
to be:
get :: (Monad m) => StateT s m s
put :: (Monad m) => s -> StateT s m ()
That means they automatically work within StateT
. transformers
then just defines State
as:
type State s = StateT s Identity
That means that you can use get
and put
for both State
and StateT
.
To learn more about monad transformers, I highly recommend Monad Transformers - Step by Step.

- 34,863
- 3
- 77
- 135
-
As always, an answer stuffed with wisdom. Thanks Gabriel, I'll have to process for a bit and see what the implications are for my application. – MFlamer Jan 25 '13 at 16:47
-
That was the perfect paper for me to read right now. I knew I was going to have to dig into Monad Transformers at some point, I just didn't realize how soon. – MFlamer Jan 26 '13 at 08:25
-
@MFlamer Yeah, Haskell is all about composing solutions from small building blocks. If monads are language building blocks (i.e. state, error handling, io), then monad transformers are composable building blocks (literally: assembling monad transformers is composition where monad transformers are monad morphisms between monad objects). When you build your own monad transformer stack, you are basically picking and choosing which language features you want to incorporate, unlike other languages which make the choice for you. – Gabriella Gonzalez Jan 27 '13 at 18:00