假设你有一组精心设计的功能,并且知道你的设计,你可以说功能和参数的组合不会发生。如果需要的话,这是编译器可以实际推动的东西。如何处理非穷尽模式匹配的误报?
为了清楚起见,拿这个例子中(不要告诉我用map
,这是一个例子):
processAll :: [Int] -> [Int]
processAll [] = []
processAll a = let (x, xs) = processOne a in x:processAll xs
where
processOne (x:xs) = (x+1,xs)
在这个例子中,这是很明显,processOne
不能与被调用空的清单。与GHC编译并加入-Wall
警告说:
Pattern match(es) are non-exhaustive
In an equation for `processOne': Patterns not matched: []
当然,我不希望禁用,一般这样的警告,因为我实际上可能错过了一个模式匹配其他地方。然而,我希望ghc能够推断出这个模式列表在其域中是详尽的。
的替代解决方案禁用警告将是:
processAll :: [Int] -> [Int]
processAll [] = []
processAll a = let (x, xs) = processOne a in x:processAll xs
where
processOne (x:xs) = (x+1,xs)
processOne _ = error "processor overheat - explosion imminent"
其是两个冗余(因为processOne []
将导致error
反正)和繁琐的。
一般人应该如何处理这种情况?继续并在每个不可能的情况下添加error
消息?
在这个特殊的例子,我知道有更好的方法通过having the caller match on the pattern来处理这一点,例如。所以,如果你想在这里是另外一个例子是从词法分析器我写,你可以运行,也非常简单摘录:
import Data.Char (isNumber, isAlpha)
import Control.Monad
data TokenType = ParenOpen -- (
| ParenClose --)
| Plus -- +
| Number String -- A number
| Variable String -- Anything else
| End -- End of the stream
deriving (Show, Eq)
-- content is the content of a file from a line and column on
type Content = (String, Int, Int)
-- a token is a token and its position as matched by the lexer
type Token = (TokenType, Int, Int)
lexer :: String -> [Token]
lexer = lexAll . (\s -> (s, 1, 1))
where
-- make a maybe value based on a Bool
makeMaybe :: Bool -> a -> Maybe a
makeMaybe p x = if p then return x else Nothing
-- advance the content by one, taking care of line and column numbers
advance :: Content -> Content
advance (x:xs, l, c) = (xs, l', c')
where
l' = if x == '\n' then l + 1 else l
c' = if x == '\n' then 1 else c + 1
-- advance the content by n
advance' n content = iterate advance content !! n
-- match a single character
matchExact :: Char -> Content -> Maybe Content
matchExact y [email protected](x:_, _, _) = makeMaybe (x == y) $ advance content
-- match while pattern holds for characters
matchPattern :: (Char -> Bool) -> Content -> Maybe (String, Content)
matchPattern p [email protected](xs, _, _) = makeMaybe (len > 0) (pfx, advance' len content)
where
pfx = takeWhile p xs
len = length pfx
matchParenOpen = matchExact '(' >=> (\c -> return (ParenOpen, c))
matchParenClose = matchExact ')' >=> (\c -> return (ParenClose, c))
matchPlus = matchExact '+' >=> (\c -> return (Plus, c))
matchNumber = matchPattern isNumber >=> (\(s, c) -> return (Number s, c))
matchVariable = matchPattern isAlpha >=> (\(s, c) -> return (Variable s, c))
lexOne :: Content -> (Token, Content)
lexOne [email protected]([], l, c) = ((End, l, c), cur)
lexOne [email protected](_, l, c) = let tokenMatchers = [matchParenOpen,
matchParenClose,
matchPlus,
matchNumber,
matchVariable
] in
case msum $ map ($ cur) tokenMatchers of
-- if nothing could be matched, generate an error and skip the character
Nothing -> lexOne $ advance cur
-- otherwise, this is an interesting token
Just (t, cnt) -> ((t, l, c), cnt)
lexAll :: Content -> [Token]
lexAll ([], _, _) = []
lexAll content = token:lexAll rest
where
(token, rest) = lexOne content
main :: IO()
main = getContents >>= putStrLn . unlines . map (\(t, l, c) -> show l ++ ":" ++ show C++ ": " ++ show t) . lexer
在上面的例子中,lexOne
确保没有任何的match*
功能,并因此advance*
函数被赋予一个Content
空字符串。 ghc
警告:
Pattern match(es) are non-exhaustive
In an equation for `advance': Patterns not matched: ([], _, _)
Pattern match(es) are non-exhaustive
In an equation for `matchExact': Patterns not matched: _ ([], _, _)
我可以肯定地说,绝对不会发生。处理这个问题的正确方法是什么?
展开cons构造函数,使它再次合计。 'processOne x xs =(x + 1,xs)'一般来说,争取所有函数在它们的类型上是全部的 - 即使它们的常规域与它们的形式域相同。 – luqui
@luqui,这是一般的好建议,我同意。但如果你快速浏览一下我的真实例子,你会发现它可能会让事情变得有些尴尬。很多只需要一个Content的函数就不得不采用'head tail(line,column)'。基本上,我定义了“内容”,以便代码更清晰。 – Shahbaz