Pure error handling in Haskell with Either: how to fold with error possibility?
either, error-handling, exception, fold, haskell
Solution
I now understand I was looking into a way to fold a list of composable actions. I came across this question, and learned about the Kleisli operator:
dereferenceValue :: ConfigParser -> String -> Either CPError String
dereferenceValue cp v = do
foldr (>=>) return (fmap replacer ["executable", "args", "title"]) v
where
replacer :: String -> String -> Either CPError String
replacer string res = do
value <- get cp "DEFAULT" string
return $ replace ("${" ++ string ++ "}") value res
Probably, this seems a bit like my question, but it feels cleaner. Particularly because of the signature of `replacer`. It doesn't receive a Monad as a second parameter and becomes more useful for other parts of code.
EDIT:
Even easier:
dereferenceValue :: ConfigParser -> String -> Either CPError String
dereferenceValue cp v = do
foldM replacer v ["executable", "args", "title"]
where
replacer :: String -> String -> Either CPError String
replacer res string = do
value <- get cp "DEFAULT" string
return $ replace ("${" ++ string ++ "}") value res
Conclusion: learn to use Hoogle
Problem
I'm mainly interested in the `Either` monad and all it's uilitites from `Control.Error`. Reading errors-1.0: Simplified error handling, I got convinced pure errors should be kept apart from IO errors. This means `error`, `fail`, `exitFailure` are functions whose use should be reduced to the IO monad. Pure computations can and will create condition errors, but are deterministic. Currently, working with folds, I got a situation an element in an array may generate a condition error which makes the whole computation unsatisfiable. For example (using Data.ConfigFile): ``` type CPError = (CPErrorData, String) data CPErrorData = ParseError String | ... type SectionSpec = String type OptionSpec = String instance Error CPError instance Error e => MonadError e (Either e) get :: MonadError CPError m => ConfigParser -> SectionSpec -> OptionSpec -> m a dereference :: ConfigParser -> String -> Either CPError String dereference cp v = foldr replacer v ["executable", "args", "title"] where replacer :: String -> Either CPError String -> Either CPError String replacer string acc = do res <- acc value <- get cp "DEFAULT" string return $ replace ("${" ++ string ++ "}") value res ``` The situation is: I'm using an acc which has a complex type, just because if a single element isn't found doing the replacement, then the whole value isn't calculable. My question is: Is this ugly? Is there a better way of doing this? I have some nastier utilities of type `EitherT CPError IO String` in the `acc` because of some IO checks.