为什么这个功能不详尽?

时间:2016-12-18 12:55:10

标签: haskell

编译器警告函数“insert”在以下代码中并非详尽无遗:

data Set a = Empty | Set a (Set a) (Set a) deriving (Eq, Show)

insert :: (Ord a) => a -> Set a -> Set a
insert x Empty = Set x Empty Empty
insert x (Set v l r)
  | x <= v = Set v (insert x l) r
  | v < x  = Set v l (insert x r)
--  | otherwise = Set x Empty Empty

main :: IO ()
main = do
  let x = insert (5::Int) Empty
  print x

GHC报告此

test.hs:4:1: warning: [-Wincomplete-patterns]
    Pattern match(es) are non-exhaustive
    In an equation for ‘insert’: Patterns not matched: _ (Set _ _ _)

如果我在函数中取消注释最后一行(现在已注释掉),GHC不会报告任何警告。所以我猜GHC认为警卫并非详尽无遗。但为什么?如果x和v是Ord的实例,那么我猜 (x <= v)和(v

1 个答案:

答案 0 :(得分:7)

如果我定义此实例怎么办:

newtype Fuzzy = Fuzzy Double
instance Eq Fuzzy where
  Fuzzy a == Fuzzy b = abs (a-b) < 0.1
instance Ord Fuzzy where
  Fuzzy a < Fuzzy b = a < b-0.1
  Fuzzy a <= Fuzzy b = a <= b

然后例如v = Fuzzy 0x = Fuzzy 0.1(x <= v) = (0.1 <= 0)为假,(v < x) = (0 < 0) 为false。因此,你的两名警卫都会失败。

这不是假设的,事实上Double本身已经has such behaviour in degenerate values

Prelude> sqrt (-1) < 0
False
Prelude> 0 <= sqrt (-1)
False

现在,关于这些是否真的很好,即使是格式良好的Ord实例也是有争议的,但无论如何编译器都可以保证之类的东西那不会发生。因此,它也不能假设not (x <= v)暗示v < x,所以应该发生什么如果两者都没有实现?

如果你假设你收到的所有Ord个实例都很温顺,那么通常要做的就是让第二个句子已经全部通过:

insert x (Set v l r)
  | x <= v     = Set v (insert x l) r
  | otherwise  = Set v l (insert x r)

但是,根据您的理念,您的原始代码可能实际上更好。如果有人拿到你的NaN值,你只需要推迟这种怪异。这使得理解正在发生的事情变得更加困难。

如果在实验代码中倾向于故意完成具有“不可能情况”的模式:这样我至少总会得到一个明确的运行时错误告诉我在代码中的哪一点出意外。一旦代码基本上有效并且你想让它准备好生产,你就可以然后-Wall中投掷,并了解你最好添加一些明确的病态处理的地方行为就像我提到的那样。