Parsers库中避免使用fail函数:Trifecta示例替代方案问询
fail in Trifecta Parsers Great question! You’re totally right that using fail isn’t recommended in Trifecta (or modern Haskell parsing libraries overall) — it relies on the MonadFail typeclass, which is being phased out because it hides error handling logic and produces unclear error messages. Let’s break down the best ways to rewrite your parserNaturalNoLeadZero without fail:
Option 1: Use unexpected for Clear Error Messages
Trifecta provides the unexpected function to explicitly signal invalid input with a custom message. This is straightforward and integrates cleanly with Trifecta’s built-in error reporting:
import Text.Trifecta parserNaturalNoLeadZero :: Parser Integer parserNaturalNoLeadZero = do digits <- some digit if length digits > 1 && head digits == '0' then unexpected "leading zero in natural number" else return $ read digits
Option 2: Use guard with <?> for Concise Checks
If you prefer more compact code, combine guard (from Control.Monad) with <?> to attach a custom error message when the guard fails:
import Text.Trifecta import Control.Monad (guard) parserNaturalNoLeadZero :: Parser Integer parserNaturalNoLeadZero = do digits <- some digit guard $ not (length digits > 1 && head digits == '0') <?> "Leading Zeros" return $ read digits
The <?> operator lets you override the default error message, making it clear exactly why the parse failed.
Option 3: Rewrite with Parser Combinators (Most Idiomatic)
For a more idiomatic parser design, avoid conditional checks entirely by defining valid input patterns directly with combinators. This makes your parser’s intent clearer and avoids explicit failure logic:
import Text.Trifecta parserNaturalNoLeadZero :: Parser Integer parserNaturalNoLeadZero = (singleDigit <|> multiDigitNoLeadingZero) >>= return . read where singleDigit = some digit -- Matches any single digit (0-9) multiDigitNoLeadingZero = do firstDigit <- satisfy (/= '0') -- First digit can't be zero restDigits <- many digit -- Followed by any number of digits return $ firstDigit : restDigits
This approach describes exactly what counts as valid input, so the parser only succeeds when the input fits the pattern — no need to check for invalid cases after the fact.
Why These Are Better Than fail
- Explicitness: All these options make error handling intentional, so anyone reading your code can see exactly when and why a parse might fail.
- Better Error Reporting: Trifecta’s built-in functions like
unexpectedand<?>produce more helpful error messages thanfail, which just uses a generic string. - Future-Proof: These methods rely on Trifecta’s core, stable features, whereas
MonadFail(and thusfailfor parsers) is being deprecated in favor of more robust error handling.
内容的提问来源于stack exchange,提问作者Răzvan Flavius Panda

