Haskell Monadic解析器开发中如何为相关函数添加错误处理
解决方案
你当前的核心问题是修改了addop/mulop的返回类型后,原有的chainl1没有对Either的错误分支做处理,两种实现方案如下:
方案1:适配你当前的修改,手动处理Either分支
你需要同步修改chainl1、term'、expr'的签名和实现,在解析运算符时判断是否返回错误:
-- 修改chainl1签名和实现,兼容返回Either的运算符 chainl1 :: Parser a -> Parser (Either String (a -> a -> a)) -> Parser (Either String a) chainl1 p op = do firstVal <- p rest firstVal where rest a = (do opRes <- op case opRes of -- 遇到错误直接向上返回 Left err -> pure $ Left err Right f -> do b <- p rest (f a b) ) <|> pure (Right a) -- 同步修改expr'和term'的签名,返回可能携带错误的解析结果 expr'::Parser (Either String Double) expr' = term' `chainl1` addop term'::Parser (Either String Double) term' = factor' `chainl1` mulop
修改完成后,addop/mulop返回的Left "错误信息"会一路传递到expr'的返回值中,你可以在解析的最终入口处统一处理错误。
方案2(更推荐):把错误能力整合到Parser Monad中
Haskell的Monad特性天然支持错误短停逻辑,不需要手动嵌套Either到返回值里:
- 如果你是自定义Parser,直接把Parser的底层定义改成支持错误的形式:
为Parser实现Monad实例时,只要某一步返回newtype Parser a = Parser { runParser :: String -> Either String (a, String) }Left,后续所有步骤都会跳过,直接向上传递错误。 - 把
addop/mulop的签名改回原来的Parser (Double -> Double -> Double),遇到错误时直接调用Parser Monad的fail方法抛出错误即可:addop = do -- 匹配运算符失败的逻辑 fail "无效的加减运算符"
这种方案不需要修改原有chainl1的实现,代码更简洁,也更符合Haskell的惯用写法。
内容的提问来源于stack exchange,提问作者Chadley Maggott
相关产品推荐
相关产品推荐

