Thanks Alberto!
I was able to derive MonadCatchIO for my stack and generalize my IO error handling to:
{-# LANGUAGE FlexibleContexts #-}
import Prelude hiding (catch)
import Control.Monad.Error
import Control.Monad.State
import Control.Monad.CatchIO
import System.IO.Error (tryIOError)
import Control.Exception (IOException)
guardIO :: (MonadCatchIO m, MonadError String m) => IO a -> m a
guardIO action =
liftIO action `catch` \e -> throwError $ show (e :: IOException)
As David mentioned it can be better to leave this to the individual, but it seems like it would be fairly common to want a drop-in replacement for liftIO that would automatically handle IO exceptions using ErrorT instead of breaking the flow of the program or requiring the developer to catch everything separately.
My example above might be too specific because not everyone will represent errors with String when using ErrorT, but we could accommodate that with:
guardIO' :: (MonadCatchIO m, MonadError e m) => IO a -> (IOException -> e) -> m a
guardIO' action convertExc =
liftIO action `catch` \e -> throwError $ convertExc eWould there be any interest in cleaning that up and adding it (or something similar) to Control.Monad.CatchIO?
Either way I will write up a blog post on it since I couldn't find any tutorials breaking this process down.