import Control.Exception import Control.Monad.Fail import Control.Monad.State import Control.Monad.IO.Class (liftIO) newtype AppState = AppState Int newtype MyApp a = MyApp ((StateT AppState IO) a) deriving (Functor, Applicative, Monad, MonadIO, MonadState AppState) runMyApp :: AppState -> MyApp a -> IO (a, AppState) runMyApp st (MyApp stT) = runStateT stT st tryMyApp :: MyApp a -> MyApp (Either SomeException a) tryMyApp job = do st <- get res <- liftIO $ try $ runMyApp st job case res of Left e -> return $ Left e Right (v, st) -> do put st ; return $ Right v throwError :: MyApp () -- error is not caught this way: -- throwError = return $ error "threw error" throwError = error "threw error" doStuff :: MyApp () doStuff = do liftIO $ putStrLn "do stuff ..." res <- tryMyApp throwError case res of Left _ -> liftIO $ putStrLn $ "catching error worked" Right v -> liftIO $ putStrLn $ "got: " ++ show v main :: IO () main = runMyApp (AppState 42) doStuff >> return ()