在Haskell中,为什么非详尽模式不是编译时错误?

时间:2010-09-27 13:54:09

标签: haskell functional-programming non-exhaustive-patterns partial-functions

这是Why am I getting "Non-exhaustive patterns in function..." when I invoke my Haskell substring function?

的后续行动

据我所知,使用-Wall,GHC可以警告不详尽的模式。我想知道在默认情况下没有使它成为编译时错误的原因是什么,因为它始终可以明确定义部分函数:

f :: [a] -> [b] -> Int
f [] _  = error "undefined for empty array"
f _ []  = error "undefined for empty array"
f (_:xs) (_:ys) = length xs + length ys

问题不是针对GHC的。

是因为......

  • 没有人想强制执行Haskell编译器来执行此类分析?
  • 非详尽模式搜索可以找到一些但不是所有情况?
  • 部分定义的函数被认为是合法的,并且经常使用不足以强加上面显示的那种构造?如果是这种情况,你能解释一下为什么非详尽的模式是有帮助/合法的吗?

2 个答案:

答案 0 :(得分:35)

有些情况下,您不介意模式匹配并非详尽无遗。例如,虽然这可能不是最佳实现,但我认为如果它不编译它会有所帮助:

fac 0 = 1
fac n | n > 0 = n * fac (n-1)

这是非详尽的(负数与任何情况都不匹配)对于阶乘函数的典型用法并不重要。

如果模式匹配是穷举的,通常也不可能决定编译器:

mod2 :: Integer -> Integer
mod2 n | even n = 0
mod2 n | odd n  = 1

这里应该涵盖所有情况,但编译器可能无法检测到它。由于防护可能是任意复杂的,编译器无法始终确定模式是否详尽无遗。当然这个例子最好用otherwise编写,但我认为它也应该以当前的形式编译。

答案 1 :(得分:14)

您可以使用-Werror将警告变为错误。我不知道你是否可以将非详尽的模式警告变成错误,抱歉!

至于问题的第三部分:

我有时会写一些函数,这些函数往往紧密地结合在一起,并具有在Haskell中无法轻易表达的属性。这些功能中的至少一些往往具有非详尽的模式,通常是“消费者”。这就出现了,例如在彼此“反转”的函数中。

玩具示例:

duplicate :: [a] -> [a]
duplicate [] = []
duplicate (x:xs) = x : x : (duplicate xs)

removeDuplicates :: Eq a => [a] -> [a]
removeDuplicates [] = []
removeDuplicates (x:y:xs) | x == y = x : removeDuplicates xs

现在很容易看到removeDuplicates (duplicate as)等于as(只要元素类型在Eq中),但一般duplicate (removeDuplicates bs)会崩溃,因为那里是奇数个元素或2个连续元素不同。如果它没有崩溃,那是因为bs首先由duplicate生成(或者可能由removeDuplicates . duplicate == id duplicate . removeDuplicates == id (for values in the range of duplicate) 生成!

所以我们有以下法律(无效的Haskell):

removeDuplicates

现在,如果您想在此处阻止非详尽模式,可以让Maybe [a]返回newtype DuplicatedList a = DuplicatedList [a] duplicate :: [a] -> DuplicatedList a removeDuplicates :: Eq a => DuplicatedList a -> [a] -- implementations omitted ,或为丢失的案例添加错误消息。你甚至可以按照

的方式做点什么
removeDuplicates

这一切都是必要的,因为在Haskell类型系统中你不能轻易地表达'是一个偶数长度的列表,连续的元素对相等'(除非你是Oleg:)

但是如果你不导出{{1}}我认为在这里使用非详尽的模式是完全可以的。一旦你导出它,你将失去对输入的控制,并将不得不处理丢失的案件!