Handling a MonadError

Hi. I'm trying to learn Data.ConfigFile. I want to use the function readfile: [DESCRIPTION] readfile :: MonadError CPError m => ConfigParser -> FilePath -> IO (m ConfigParser) Loads data from the specified file. It is then combined with the given ConfigParser using the semantics documented under merge with the new data taking precedence over the old. However, unlike merge, all the options as set in the old object are preserved since the on-disk representation does not convey those options. May return an error if there is a syntax error. May raise an exception if the file could not be accessed. [/DESCRIPTION] I understand that this function returns a monad, and I pull the value out of it with (<-). And I believe this is similar to a "Maybe" situation where I can do one thing if it provides a syntax error and another thing if it provides the ConfigParser. But I'm not sure what this would look like in actual code. Could somebody give me a small, simple example? -- frigidcode.com theologia.indicium.us

On 30 May 2011 05:05, Christopher Howard
Hi. I'm trying to learn Data.ConfigFile. I want to use the function readfile:
[DESCRIPTION] readfile :: MonadError CPError m => ConfigParser -> FilePath -> IO (m ConfigParser)
I understand that this function returns a monad, and I pull the value out of it with (<-). And I believe this is similar to a "Maybe" situation where I can do one thing if it provides a syntax error and another thing if it provides the ConfigParser. But I'm not sure what this would look like in actual code. Could somebody give me a small, simple example?
It's understandable that actual usage isn't clear, it wasn't clear to me either when I first encountered it. There are a few ways to use it. It returns an instance of error Monad, so you can either force it into an Either value: λ> do parser <- readfile emptyCP "../confy.conf"; return (get (forceEither parser) "PATHS" "upload_path") :: IO (Either CPError String) Right "uploads" Or you can get it within the monad like everything else: λ> do parser <- readfile emptyCP "../confy.conf"; return (do config <- parser; get config "PATHS" "upload_path") :: IO (Either CPError String) Right "uploads" Because it returns any MonadError instance, and IO is an instance of MonadError, you can skip the double level of monadery: λ> let getUploads :: IO (Either CPError String); getUploads = runErrorT $ do parser <- liftIO $ readfile emptyCP "../confy.conf"; config <- parser; get config "PATHS" "upload_path" in getUploads Right "uploads" Or with join: λ> :t join join :: (Monad m) => m (m a) -> m a λ> let getUploads :: IO (Either CPError String); getUploads = runErrorT $ do cp <- join $ liftIO $ readfile emptyCP "../confy.conf"; get cp "PATHS" "upload_path" in getUploads Right "uploads" Control.Applicative also works nicely for this. instance Applicative (ErrorT CPError IO) where (<*>) = ap; pure = return λ> let getCfg :: IO (Either CPError (String,String)); getCfg = runErrorT $ do cp <- join $ liftIO $ readfile emptyCP "../confy.conf"; (,) <$> get cp "PATHS" "upload_path" <*> get cp "PATHS" "err_log" in getCfg Right ("uploads","errors.log")

Related question: Could you explain the typeclass definition for MonadError to me?: class (Monad m) => MonadError e m | m -> e where throwError :: e -> m a catchError :: m a -> (e -> m a) -> m a More specifically: Why is there a pipe symbol followed by what looks like a function definition in the "class" line? The typeclass definitions from the tutorials I went through were simpler, like: class BasicEq a where isEqual :: a -> a -> Bool -- frigidcode.com theologia.indicium.us

On 30 May 2011 20:35, Christopher Howard
class (Monad m) => MonadError e m | m -> e where throwError :: e -> m a catchError :: m a -> (e -> m a) -> m a
More specifically: Why is there a pipe symbol followed by what looks like a function definition in the "class" line?
It is a functional dependency. Generally you can read it as "m uniquely determines e".

On 30 May 2011 22:17, Stephen Tetley
On 30 May 2011 20:35, Christopher Howard
More specifically: Why is there a pipe symbol followed by what looks like a function definition in the "class" line?
It is a functional dependency.
Generally you can read it as "m uniquely determines e".
The simplest instance seems to be this one: instance (Error e) => MonadError e (Either e) where throwError = Left Left l `catchError` h = h l Right r `catchError` _ = Right r The functional dependency tells GHC that the error parameter 'e' is the same type variable 'e' that is in monad parameter '(Either e)'
participants (3)
-
Christopher Done
-
Christopher Howard
-
Stephen Tetley