我主要对 Either
感兴趣monad 和所有它的 uilitites 来自 Control.Error
.阅读 errors-1.0: Simplified error handling ,我确信应该将纯错误与 IO 错误分开。这意味着 error
, fail
, exitFailure
是应简化为 IO monad 的函数。纯计算可以并且将会产生条件错误,但是是确定性的。
目前,与 合作折叠 ,我遇到了一种情况,数组中的元素可能会产生条件错误,从而使整个计算无法满足。例如(使用 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,只是因为如果没有找到单个元素进行替换,那么整个值是不可计算的。
我的问题是:这丑吗? 有没有更好的方法来做到这一点? 我有一些
EitherT CPError IO String
类型的更糟糕的实用程序在 acc
因为一些 IO 检查。
最佳答案
我现在明白我正在寻找一种折叠可组合 Action 列表的方法。我遇到了this问题,了解了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
可能,这看起来有点像我的问题,但感觉更干净。特别是因为
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
关于haskell - Haskell 中的纯错误处理,带有 : how to fold with error possibility?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23984923/