
On Fri, Apr 15, 2005 at 08:17:13PM -0700, Isaac Jones wrote:
Can anyone help me explain this behavior?
I'm not sure this will be a satisfactory explanation, but if you look at StateT's implementation it is quite obvious that is must work this way: newtype StateT s m a = StateT {runStateT :: (s -> m (a, s))} for m = IO and s = Int this is StateT {runStateT :: (Int -> IO (a, Int))} The new state is returned as part of IO action result. If there is an exception, you don't get the new state.
Is this because it's not really "safe" to embed the IO monad in a StateT monad?
I guess it depends on what you mean by "safe". In some situations this might be the expected behaviour.
The strange thing I see is that the errorHandler function is the only one whose modifications to the state persist. If this is a problem, maybe try catching the exception deeper, for example: (s, n) <- runStateT (stateFun 1 >> catchError stateErr errorHandler >> stateFun 10) 0
Best regards Tomasz