
I wrote:
Combining ST and MTL can be messy, even in this simple case. You will probably write something with a type like RandomGen g => [a] -> g -> ST s ([a], g)
Udo Stenzel wrote:
But why would you even want to do this? It's ugly and cumbersome.
Yes indeed.
You'd plug a runST in there and get shuffle :: RandomGen g => [a] -> g -> ([a], g)
Yes. In fact, that is what I did in practice. As you say, the overall effect is ugly and cumbersome. And this is with only the simplest of stateful calculations. I shudder to think about what happens when things are more complex. That is why I am thinking that -
Wouldn't it be nice if instead you could just write:
shuffle :: (RandomGen g, MonadState g m) => [a] -> m [a] shuffle = stToState . shuffleST
and then just use that directly inside a calculation that is otherwise purely non-ST?
It seems, what you really want is shuffleST :: RandomGen g => [a] -> StateT g ST [a]
Actually, I tried that. It didn't help - it was just one more layer I had to peel away to get at the ST inside. There seems to be no way to avoid the fact that you think about state in two very different ways in these two monads. Every program is written in either one style or the other. Occasionally, you require an isolated use of the opposite style, and I am looking for ways of simplifying the resulting mess. "StateT st ST" and "MonadST" look like ways of combining the two, but in practice I find that they just seem to get in the way. I am starting to be convinced that the only way to write the function I want is by using unsafeRunST. Or type it as stToState :: MonadState st m => (st -> ST s (a, st)) -> m a and then write in the documentation that the user is require to write do r <- newSTRef x ... y <- readSTRef r return (z, y) by hand every time. Yuck. Am I missing something? -Yitz