I am a little bit confused of the usage of ExceptT. It seems that this monad transformer add the ability to handle exceptions. But the problem is that it does not work very well with StateT. I modified an example from StackOverflow.
import Control.Applicative
import Control.Monad.Error
import Control.Monad.State
import Control.Monad.Trans.Except
import Data.Functor.Identity
test1 :: (MonadState Int m, MonadError String m) => m Bool
test1 = do
put 1
_ <- throwError "foobar"
put 2
return False
test2 :: (Alternative m, MonadState Int m, MonadError String m) => m Bool
test2 = do
put 4
test1 <|> return True
test3 :: (Alternative m, MonadState Int m, MonadError String m) => m Bool
test3 = do
let handle = do
x <- get
if x == 4
then put 40
else put 50
return True
put 4
test1 <|> handle
runStateExceptT :: s -> ExceptT e (StateT s m) a -> m (Either e a, s)
runStateExceptT s = flip runStateT s . runExceptT
runExceptStateT :: s -> StateT s (ExceptT e m) a -> m (Either e (a, s))
runExceptStateT s = runExceptT . flip runStateT s
main :: IO ()
main = do
print $ runIdentity . runStateExceptT 3 $ test1
print $ runIdentity . runExceptStateT 3 $ test1
print $ runIdentity . runStateExceptT 3 $ test2
print $ runIdentity . runExceptStateT 3 $ test2
print $ runIdentity . runStateExceptT 3 $ test3
print $ runIdentity . runExceptStateT 3 $ test3
and the result is
(Left "foobar",1)
Left "foobar"
(Right True,1)
Right (True,4)
(Right True,50)
Right (True,40)
It seems that ExceptT can only restore state that is “outside” the ExceptT. Otherwise it will carry wrong state around even if there is an exception.
My question is
- What is the proper order to use
ExceptT? Should I always put it to the innermost position. - Does it guarantee to stop execution once an exception is thrown? And is this ability affected by the position of it in the monad stack?
Thank you!