I did this in Haskell using MPTC, I have used the Convertible class like:
instance Convertible IOError RedisError where safeConvert = <conversion mapping here>
Then I have combinators like:
convEither :: Convertible e1 e2 => Either e1 a -> Either e2 a
and
convEitherT :: Convertible e1 e2 => m (Either e1 a) -> EitherT m e2 a
These are quite handy in close quarters combat. For example to open a file and bail on a generic IOException with a custom application exception, I can have something like:
instance Convertible IOException CustomError
getWords :: FilePath -> IO (Either CustomError [String])
getWords fileName = runEitherT $
do file <- tryIO (openFile fileName) >>= convEitherT
line <- readLine file
return (words line)