
Hi, I wrote some code and I want to refactor it to use the Error monad combined with IO operations. I found some examples of monad transformers but I didn't really get them. So I have a function in IO monad which does several operations which might fail. These operations work with files so they have to be in IO monad but on the other hand I would like to use Error monad such that I can throw errors. So far I came up with something like this. data Config = Config { root :: String } data ParsedData = ParsedData {...} data MyError = ConfigError | ParseError | OtherError String instance Error MyError where strMsg = OtherError loadConfig :: String -> Either MyError (IO Config) loadConfig fn -> ...might throwError ConfigError parseFile :: String -> Either MyError (IO ParsedData) parseFile fn -> .. might throwError ParseError main -> do config <- loadConfig "mycfg.cfg" parsedData <- parseFile (root config) ... Can somebody please enlighten me about how to apply monad transformers in this situation? Thanks, Ovidiu

On Mon, 22 Aug 2011 19:51:18 +0300, Ovidiu Deac
Hi,
I wrote some code and I want to refactor it to use the Error monad combined with IO operations. I found some examples of monad transformers but I didn't really get them.
So I have a function in IO monad which does several operations which might fail. These operations work with files so they have to be in IO monad but on the other hand I would like to use Error monad such that I can throw errors.
For this you need to use a monad transformer instead, in this case ErrorT. Monad transformers are given an "inner monad", in this case IO, and usually the same parameters the "original" monad, in this case Either, expected. A monad transformer normally comes with a runFooT function that returns a value in the inner monad. RWH has a chapter on monad transformers that explains them more clearly: http://book.realworldhaskell.org/read/monad-transformers.html Here's a shortened version of your example: import Control.Monad.Error data Config = Config deriving Show data MyError = ConfigError | ParseEerror | OtherError String deriving Show instance Error MyError where strMsg = OtherError loadConfig :: String -> ErrorT MyError IO Config loadConfig _ = do lift $ putStrLn "foo" -- `lift' is necessary to turn an IO action into an ErrorT action throwError ConfigError main = print =<< runErrorT (loadConfig "foo") The result type in your code, "Either MyError (IO Config)", would mean that the function decides, without doing any IO, whether or not to return an error and, if not, return an IO action that will read the config. Cheers, Daniel

Hi. Daniel already answered the question; here is an alternative definition for the loadConfig function he gave. Uses type-classes and liftIO. I find it easier to use type-classes when dealing with monad transformers, especially if you have more than a couple of them. You get automatic lifting for every monad transformer in the stack except IO, for which you need to use liftIO. Please ask if you have any questions. loadConfig :: (MonadError MyError m, MonadIO m) => String -> m Config loadConfig _ = do liftIO $ putStrLn "foo" throwError ConfigError HTH, Ozgur
participants (3)
-
Daniel Schoepe
-
Ovidiu Deac
-
Ozgur Akgun