
I've been experimenting with the state monad and with StateT, and have some questions about how to combine one state with another. This email is literate Haskell tested on GHCi, version 6.10.1. Also, sigfpe's post on monad transformers (http://blog.sigfpe.com/2006/05/ grok-haskell-monad-transformers.html) was very helpful.
import Control.Monad.State
My question is basically whether the function modifyT (below) makes sense, whether some form of it already exists in a standard library, and (most importantly) whether it actually indicates that I'm thinking about StateT all wrong.
modifyT :: Monad m => (s -> StateT t m s) -> StateT t (StateT s m) () modifyT f = do x <- get y <- lift get (y',x') <- lift $ lift $ runStateT (f y) x lift $ put y' put x'
Some context may be useful, so here is how I ended up thinking I needed modifyT. The state monad makes it easy to write stateful computations. For example here is a computation that has an Integer as its state and returns a String:
test1 :: State Integer String test1 = do modify (+ 1) a <- get return $ "foobar" ++ (show a)
If the computation wants to do some IO then it makes sense to start with the IO monad and then apply the StateT transformer to it:
test2 :: StateT Integer IO String test2 = do modify (+ 1) a <- get lift $ print a return $ "foobar" ++ (show a)
So from now on I won't actually do any IO and will replace IO with an arbitrary monad m. Also instead of the fixed string "foobar" I'll have it take a String as a parameter:
test3 :: Monad m => String -> StateT Integer m String test3 s = do modify (+ 1) a <- get return $ s ++ (show a)
A nice feature of all this is that it is easy to combine these computations:
test4 :: Monad m => StateT Integer m (String,String) test4 = do s1 <- test3 "foo" s2 <- test3 "bar" return $ (s1,s2)
Now seeing as test3 takes a String and returns another String you can imagine using it to transform a String state. (I'm also going to assume that test3 is in another library so we don't want to alter how it's written.) So here is how you could use test3 in a computation that has (String,Integer) as its state:
test5 :: (Monad m) => m Integer test5 = do (s1,x1) <- runStateT (test3 "") 0 (s2,x2) <- runStateT (test3 s1) (2*x1 + 1) (s3,x3) <- runStateT (test3 s2) (x2*x2) return x3
Then running test5 >>= print gives 17. The problem with test5, of course, is that we have manually threaded the state, with all the problems that implies. For example nothing prevents you from erroneously misthreading the state:
test5bad :: (Monad m) => m Integer test5bad = do (s1,x1) <- runStateT (test3 "") 0 (s2,x2) <- runStateT (test3 s1) (2*x1 + 1) (s3,x3) <- runStateT (test3 s1) (x2*x1) return x3
Running test5bad >>= print gives 5. Obviously we want operate in a State monad with more state. One way to do this is to stack two StateTs on top of m. This is, finally, where I need the modifyT that we defined above -- it lets us "lift" test3 to a function that modifies the state of the top *two* StateTs. Now let's use it to rewrite test5:
test6 :: (Monad m) => StateT Integer (StateT String m) Integer test6 = do modifyT test3 modify $ \x -> 2*x + 1 modifyT test3 modify $ \x -> x*x modifyT test3 x <- get return x
test7 :: (Monad m) => m Integer test7 = evalStateT (evalStateT test6 0) ""
As expected, running test7 >>= print gives 17. So, given that modifyT seems to be useful, does it, or something like it, already exists in the standard libraries? More likely, am I making a mountain of a molehill and is there a better way to structure all this? Thanks, Luis
main = do test5 >>= print test5bad >>= print test7 >>= print

On Sat, Feb 21, 2009 at 3:33 PM, Luis O'Shea
I've been experimenting with the state monad and with StateT, and have some questions about how to combine one state with another.
<snip>
test3 :: Monad m => String -> StateT Integer m String test3 s = do modify (+ 1) a <- get return $ s ++ (show a)
A style point: It's often better to specify what operations test3 uses, rather than requiring a specific family of monads. test3 :: MonadState Integer m => String -> m String
Now seeing as test3 takes a String and returns another String you can imagine using it to transform a String state. (I'm also going to assume that test3 is in another library so we don't want to alter how it's written.) So here is how you could use test3 in a computation that has (String,Integer) as its state:
test5 :: (Monad m) => m Integer test5 = do (s1,x1) <- runStateT (test3 "") 0 (s2,x2) <- runStateT (test3 s1) (2*x1 + 1) (s3,x3) <- runStateT (test3 s2) (x2*x2) return x3
You don't really need to jump all the way out of the state
transformer. Something like this would work just as well:
test5a = flip execStateT 0 $ do
s1 <- test3 ""
modify $ \x -> 2 * x + 1
s2 <- test3 s1
modify $ \x -> x * x
test3 s2
Contrast this with your test6.
Now, if you want to avoid passing the strings around explicitly, you
could add another state transformer. For example, we could layer a
String transformer on top of the underlying monad with this fairly
general combinator:
modifyM :: (Monad m) => (s -> m s) -> StateT s m ()
modifyM f = StateT $ \s -> f s >>= \s' -> return ((),s')
test5b = flip execStateT 0 . flip evalStateT "" $ do
modifyM test3
lift $ modify $ \x -> 2 * x + 1
modifyM test3
lift $ modify $ \x -> x * x
modifyM test3
Note that modifyM works on the top-level state, whereas lift . modify
works on the inner state.
Or, you can put the state transformer on the bottom by taking
advantage of the fact that test3 is polymorphic in any underlying
monad.
test3' = lift get >>= test3 >>= lift . put -- this is essentially
modifyT test3
test5c = flip evalState "" . flip execStateT 0 $ do
test3'
modify $ \x -> 2*x+1
test3'
modify $ \x -> x * x
test3'
But this only really makes sense if you expect the String state to
last at least as long as the Integer state.
Of my three alternatives, test5a actually seems the most idiomatic to
me, followed by test5b and then test5c. It's possible to write test5a
in a way that avoids explicitly passing the strings around, but the
result doesn't end up looking much better.
PS. Here are two functions that I ended up not using in my examples,
but which may come in handy when dealing with nested applications of
StateT:
curryStateT :: (Monad m) => StateT (s,t) m a -> StateT s (StateT t m) a
curryStateT m = StateT $ \s -> StateT $ \t ->
runStateT m (s,t) >>= \ ~(a,(s,t)) -> return ((a,s),t)
uncurryStateT :: (Monad m) => StateT s (StateT t m) a -> StateT (s,t) m a
uncurryStateT m = StateT $ \ ~(s,t) ->
runStateT (runStateT m s) t >>= \ ~((a,s),t) -> return (a,(s,t))
--
Dave Menendez

On Sat, Feb 21, 2009 at 5:37 PM, David Menendez
PS. Here are two functions that I ended up not using in my examples, but which may come in handy when dealing with nested applications of StateT:
curryStateT :: (Monad m) => StateT (s,t) m a -> StateT s (StateT t m) a curryStateT m = StateT $ \s -> StateT $ \t -> runStateT m (s,t) >>= \ ~(a,(s,t)) -> return ((a,s),t)
uncurryStateT :: (Monad m) => StateT s (StateT t m) a -> StateT (s,t) m a uncurryStateT m = StateT $ \ ~(s,t) -> runStateT (runStateT m s) t >>= \ ~((a,s),t) -> return (a,(s,t))
For some reason that reminds me of one of my favorites: embedReader :: Monad m => ReaderT r m a -> Reader r (m a) embedReader m = Reader $ \r -> runReaderT m r Antoine

test3 :: MonadState Integer m => String -> m String
Good point. It's interesting that this allows the signature of test5b to become MonadState Integer m => m Integer (instead of (Monad m) => StateT Integer (StateT String m) Integer) which is more general, and (surprisingly to me) does not mention String. Thank! Luis

On Sun, Feb 22, 2009 at 9:20 AM, Luis O'Shea
test3 :: MonadState Integer m => String -> m String
Good point. It's interesting that this allows the signature of test5b to become MonadState Integer m => m Integer (instead of (Monad m) => StateT Integer (StateT String m) Integer) which is more general, and (surprisingly to me) does not mention String.
Odd. If I break up test5b like so:
test5b = flip execStateT 0 . flip evalStateT "" $ test5bImpl
test5bImpl = do
modifyM test3
lift . modify $ \x -> x*2 + 1
modifyM test3
lift . modify $ \x -> x*x
modifyM test3
and ask GHCi for the types, I get:
*Main> :t test5bImpl
test5bImpl :: (MonadState Integer m) => StateT String m ()
*Main> :t test5b
test5b :: (Monad m) => m Integer
--
Dave Menendez

Am Sonntag, 22. Februar 2009 18:53 schrieb David Menendez:
On Sun, Feb 22, 2009 at 9:20 AM, Luis O'Shea
wrote: test3 :: MonadState Integer m => String -> m String
Good point. It's interesting that this allows the signature of test5b to become MonadState Integer m => m Integer (instead of (Monad m) => StateT Integer (StateT String m) Integer) which is more general, and (surprisingly to me) does not mention String.
Odd. If I break up test5b like so:
test5b = flip execStateT 0 . flip evalStateT "" $ test5bImpl
test5bImpl = do modifyM test3 lift . modify $ \x -> x*2 + 1 modifyM test3 lift . modify $ \x -> x*x modifyM test3
and ask GHCi for the types, I get:
*Main> :t test5bImpl test5bImpl :: (MonadState Integer m) => StateT String m () *Main> :t test5b test5b :: (Monad m) => m Integer
Okay, you've found a bug in the type checker, 6.4.2 infers the types test5b :: (Monad (StateT [Char] (StateT s m)), MonadState s (StateT s m), Num s, Monad m) => m s test5bImpl :: (Monad (StateT [Char] m), MonadState s m, Num s) => StateT [Char] m () as does 6.6.1. That does look more reasonable.

On Sun, Feb 22, 2009 at 3:17 PM, Daniel Fischer
test5b :: (Monad (StateT [Char] (StateT s m)), MonadState s (StateT s m), Num s, Monad m) => m s
Doesn't 'Monad m' imply 'MonadState s (StateT s m)' which implies 'Monad (StateT s m)' which implies 'Monad (StateT [Char] (StateT s m))'? This signature could be simplified to just test5b :: (Num s, Monad m) => m s
test5bImpl :: (Monad (StateT [Char] m), MonadState s m, Num s) => StateT [Char] m ()
Here 'MonadState s m' implies 'Monad m' which implies 'Monad (StateT [Char] m)', simplifying the signature to test5bImpl :: (Num s, MonadState s m) => StateT [Char] m () Now, note that Menendez' signatures didn't have 'Num s' because a type signature was given to 'test3' forcing the number to be Integer. If you remove all type signatures (and the monomorphism restriction), GHC 6.10 infers modifyM :: (Monad m) => (s -> m s) -> StateT s m () test3 :: (Num s, MonadState s m) => [Char] -> m [Char] test5b :: (Num b, Monad m) => m b test5b' :: (Num b, Monad m) => m b impl :: (Num s, MonadState s m) => StateT [Char] m () Did I make any mistakes? -- Felipe.

Luis O'Shea wrote:
One way to do this is to stack two StateTs on top of m.
Another way, what might be easier to reason about, is to crush those two layers together and use a tuple for the state: StateT s1 (StateT s2 m) a == StateT (s1,s2) m a Then the only thing you'll have to worry about is making sure the different "clients" only access s1 or s2 as appropriate. One approach to access control is to make a newtype S = (s1,s2) and define a typeclass taking S and returning the different projections of it (e.g. s1 or s2). This way if you needed to add another StateT layer you can just adjust the definition of S and add a new typeclass instance. Similarly if you wanted to rearrange the contents of S. This also works if you want to have different S, S', etc ---even if they share projections--- though you'll need to use MPTCs (or similar) so you can overload on the total state as well as the projections. The downside to the typeclass approach is that you can't have two projections (of the same S) sM and sN which are the same type. If you want that you'll need to newtype one of the projections, manually pass around the projector dictionaries, or similar. -- Live well, ~wren
participants (6)
-
Antoine Latter
-
Daniel Fischer
-
David Menendez
-
Felipe Lessa
-
Luis O'Shea
-
wren ng thornton