为什么非详尽的警卫导致无可辩驳的模式匹配失败?

时间:2011-08-18 14:42:21

标签: haskell pattern-matching guard

我在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

根据Real World Haskell的说法,第一种模式是无可辩驳的。但似乎test 3 4没有失败第一个模式,并匹配第二个模式。我期待某种错误 - 也许是'非详尽的警卫'。那么这里真正发生了什么,是否有一种方法可以在发生意外情况时启用编译器警告?

3 个答案:

答案 0 :(得分:11)

第一种模式确实是一种“无可辩驳的模式”,但这并不意味着它总会选择相应的功能右侧。它仍然受到可能失败的警卫的影响。

为了确保涵盖所有案例,通常使用otherwise来获得最终成功的后卫。

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

请注意otherwise没有任何魔力。它在前奏中定义为otherwise = True。但是,在最终案例中使用otherwise是惯用的。

在一般情况下让编译器警告非执行保护是不可能的,因为它涉及解决暂停问题,但是存在像Catch这样的工具,它们试图比编译器做得更好。确定在所有案件中是否涵盖所有案件。

答案 1 :(得分:6)

如果你遗漏第二个条款,编译器应该警告你,即如果你的最后一个匹配有一组守卫,其中最后一个不是一般的真实。

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

回答马特的评论:

看一下例子:

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不是底部,那么p x对于所有可能的x都是True。换句话说,无论p x是什么还是评估为x,它都必须证明True是底部(不停止)。

答案 2 :(得分:1)

守卫不是无可辩驳的。但是,添加一个能够捕捉其他情况的最后一个守卫是非常普遍的(也是很好的)练习,所以你的功能变成了:

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