为什么非穷举防护会导致无可辩驳的模式匹配失败?
我在 Haskell 中有这个函数:
test :: (Eq a) => a -> a -> Maybe a
test a b
| a == b = Just a
test _ _ = Nothing
这是当我尝试使用不同输入的函数时得到的结果:
ghci>test 3 4
Nothing
ghci>test 3 3
Just 3
根据现实世界的 Haskell,第一个模式是无可辩驳的。但似乎 test 3 4
没有使第一个模式失败,并且匹配第二个模式。我预计会出现某种错误——也许是“非详尽的守卫”。那么这里到底发生了什么,有没有办法启用编译器警告,以防意外发生?
I have this function in Haskell:
test :: (Eq a) => a -> a -> Maybe a
test a b
| a == b = Just a
test _ _ = Nothing
This is what I got when I tried the function with different inputs:
ghci>test 3 4
Nothing
ghci>test 3 3
Just 3
According to Real World Haskell, the first pattern is irrefutable. But it seems like test 3 4
doesn't fails the first pattern, and matches the second. I expected some kind of error -- maybe 'non-exhaustive guards'. So what is really going on here, and is there a way to enable compiler warnings in case this accidentally happens?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
第一个模式确实是一个“无可辩驳的模式”,但这并不意味着它总是选择函数相应的右侧。它仍然受到警卫的影响,这可能会像您的示例中那样失败。
为了确保覆盖所有情况,通常使用
otherwise
来拥有一个始终会成功的最终防护。请注意,
otherwise
并没有什么神奇之处。它在 Prelude 中被定义为otherwise = True
。但是,对于最终情况,通常使用otherwise
。在一般情况下,让编译器警告非详尽的保护是不可能的,因为这将涉及解决停止问题,但是存在诸如 Catch 试图比编译器做得更好,以确定在常见情况下是否涵盖所有情况。
The first pattern is indeed an "irrefutable pattern", however this does not mean that it will always choose the corresponding right hand side of your function. It is still subject to the guard which may fail as it does in your example.
To ensure all cases are covered, it is common to use
otherwise
to have a final guard which will always succeed.Note that there is nothing magic about
otherwise
. It is defined in the Prelude asotherwise = True
. However, it is idiomatic to useotherwise
for the final case.Having a compiler warn about non-exaustive guards would be impossible in the general case, as it would involve solving the halting problem, however there exist tools like Catch which attempt to do a better job than the compiler at determining whether all cases are covered or not in the common case.
如果您遗漏了第二个子句,即如果您的最后一个匹配有一组守卫,其中最后一个不完全正确,编译器应该警告您。
一般来说,测试守卫的完整性显然是不可能的,因为这与解决停止问题一样困难。
回答马特的评论:
看这个例子:
一个人可以看到两个守卫之一一定是真的。但编译器不知道
a <= b
或a > 。 b.
。现在看另一个例子:
为了证明守卫集合是完整的,编译器必须证明费马大定理。在编译器中不可能做到这一点。请记住,防护装置的数量和复杂性是不受限制的。编译器必须是数学问题的通用求解器,这些问题在 Haskell 本身中陈述。
更正式地说,在最简单的情况下:
编译器必须证明如果
p x
不是底部,则对于所有可能的 x,p x
都是True
。换句话说,它必须证明无论x
是什么或计算结果为True
,p x
都是底部(不会停止)。The compiler should be warning you if you leave out the second clause, i.e. if your last match has a set of guards where the last one is not trivially true.
Generally, testing guards for completeness is obviously not possible, as it would be as hard as solving the halting problem.
Answer to Matt's comment:
Look at the example:
A human can see that one of both guards must be true. But the compiler does not know that either
a <= b
ora > b
.Now look for another example:
To prove that the set of guards is complete, the compiler had to prove Fermat's Last Theorem. It's impossible to do that in a compiler. Remember that the number and complexity of the guards is not limited. The compiler would have to be a general solver for mathematical problems, problems that are stated in Haskell itself.
More formally, in the easiest case:
the compiler must prove that if
p x
is not bottom, thenp x
isTrue
for all possible x. In other words, it must prove that eitherp x
is bottom (does not halt) no matter whatx
is or evaluates toTrue
.守卫并不是无可辩驳的。但是添加最后一个防护来捕获其他情况是非常常见(也是好的)做法,因此您的函数变为:
Guards aren't irrefutable. But is very common (and good) practise to add one last guard that catch the other cases, so your function becomes: