我在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
没有失败第一个模式,并匹配第二个模式。我期待某种错误 - 也许是'非详尽的警卫'。那么这里真正发生了什么,是否有一种方法可以在发生意外情况时启用编译器警告?
答案 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 <= b
或a > 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