8

我主要对Eithermonad 和它的所有 uilitites 感兴趣Control.Error。阅读errors-1.0:简化的错误处理,我确信应该将纯错误与IO 错误分开。这意味着error,failexitFailure应简化为 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

4

2 回答 2

8

我现在明白我正在寻找一种折叠可组合动作列表的方法。我遇到了这个问题,并了解了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

于 2014-06-01T22:52:26.670 回答
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
于 2014-06-02T07:31:20.830 回答