为什么非穷举防护会导致无可辩驳的模式匹配失败?

发布于 2024-11-30 12:26:33 字数 390 浏览 1 评论 0原文

我在 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

舂唻埖巳落 2024-12-07 12:26:33

第一个模式确实是一个“无可辩驳的模式”,但这并不意味着它总是选择函数相应的右侧。它仍然受到警卫的影响,这可能会像您的示例中那样失败。

为了确保覆盖所有情况,通常使用 otherwise 来拥有一个始终会成功的最终防护。

test :: (Eq a) => a -> a -> Maybe a
test a b
  | a == b    = Just a
  | otherwise = Nothing

请注意,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.

test :: (Eq a) => a -> a -> Maybe a
test a b
  | a == b    = Just a
  | otherwise = Nothing

Note that there is nothing magic about otherwise. It is defined in the Prelude as otherwise = True. However, it is idiomatic to use otherwise 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.

双马尾 2024-12-07 12:26:33

如果您遗漏了第二个子句,即如果您的最后一个匹配有一组守卫,其中最后一个不完全正确,编译器应该警告您。

一般来说,测试守卫的完整性显然是不可能的,因为这与解决停止问题一样困难。

回答马特的评论:

看这个例子:

foo a b 
   | a <= b = True
   | a >  b = False

一个人可以看到两个守卫之一一定是真的。但编译器不知道 a <= ba > 。 b.

现在看另一个例子:

fermat a b c n 
    | a^n + b^n /= c^n = ....
    | n < 0 = undefined
    | n < 3 = ....

为了证明守卫集合是完整的,编译器必须证明费马大定理。在编译器中不可能做到这一点。请记住,防护装置的数量和复杂性是不受限制的。编译器必须是数学问题的通用求解器,这些问题在 Haskell 本身中陈述。

更正式地说,在最简单的情况下:

 f x | p x = y

编译器必须证明如果 p x 不是底部,则对于所有可能的 x,p x 都是 True。换句话说,它必须证明无论 x 是什么或计算结果为 Truep 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:

foo a b 
   | a <= b = True
   | a >  b = False

A human can see that one of both guards must be true. But the compiler does not know that either a <= b or a > b.

Now look for another example:

fermat a b c n 
    | a^n + b^n /= c^n = ....
    | n < 0 = undefined
    | n < 3 = ....

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:

 f x | p x = y

the compiler must prove that if p x is not bottom, then p x is True for all possible x. In other words, it must prove that either p x is bottom (does not halt) no matter what x is or evaluates to True.

乖不如嘢 2024-12-07 12:26:33

守卫并不是无可辩驳的。但是添加最后一个防护来捕获其他情况是非常常见(也是好的)做法,因此您的函数变为:

test :: (Eq a) => a -> a -> Maybe a
test a b
  | a == b = Just a
  | True = Nothing

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:

test :: (Eq a) => a -> a -> Maybe a
test a b
  | a == b = Just a
  | True = Nothing
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文