我主要感谢Either
monad及其所有来自Control.Error
的uilitites。阅读errors-1.0: Simplified error handling,我确信纯错误应该与 IO错误分开。这意味着error
,fail
,exitFailure
是其功能应减少为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
的实用工具。
答案 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