Haskell中的纯错误处理要么:如何折叠错误的可能性?

时间:2014-06-01 21:58:02

标签: haskell exception-handling error-handling fold either

我主要感谢Either monad及其所有来自Control.Error的uilitites。阅读errors-1.0: Simplified error handling,我确信纯错误应该与 IO错误分开。这意味着errorfailexitFailure是其功能应减少为IO monad的函数。纯计算可以创建条件错误,但确定性

目前,使用 folds ,我遇到一种情况,数组中的元素可能会生成条件错误,这使得整个计算不可满足。例如(使用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

情况是:我使用的是具有复杂类型的acc,只是因为如果找不到单个元素进行替换,则整个值无法计算。

我的问题是:这难看吗? 有更好的方法吗?由于某些IO检查,我在EitherT CPError IO String中有一些类型为acc的实用工具。

2 个答案:

答案 0 :(得分:6)

我现在明白我正在研究折叠可组合动作列表的方法。我遇到了this问题,并了解了Kleisli运算符:

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

可能这看起来有点像我的问题,但感觉更干净。特别是因为replacer的签名。 它没有收到Monad 作为第二个参数,并且变得对代码的其他部分更有用

修改

更容易:

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

结论:学会使用Hoogle

答案 1 :(得分:4)

正如您所发现的,foldM在这里工作得非常好。您的replacer功能

replacer :: String -> String -> Either String String
replacer res string = do
  value <- get cp "DEFAULT" string
  return $ replace ("${" ++ string ++ "}") value res

可以使用Applicative进一步美化如下

replacer :: String -> String -> Either String String
replacer res string =
  replace ("${" ++ string ++ "}") <$> get cp "DEFAULT" string <*> pure res