32

这是我调用 Haskell 子字符串函数时为什么会出现“函数中的非详尽模式...”的后续内容?

我知道使用-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 编译器执行这种分析?
  • 非详尽的模式搜索可以找到一些但不是所有的情况?
  • 部分定义的函数被认为是合法的,并且经常使用到不会强加上面显示的那种构造?如果是这种情况,您能否向我解释为什么非详尽的模式是有帮助/合法的?
4

2 回答 2

38

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

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,但我认为它也应该以当前形式编译。

于 2010-09-27T14:26:20.413 回答
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!。

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

removeDuplicates . duplicate == id
duplicate . removeDuplicates == id (for values in the range of duplicate)

现在,如果您想在此处防止非详尽模式,您可以removeDuplicates返回Maybe [a],或为丢失的情况添加错误消息。你甚至可以做一些类似的事情

newtype DuplicatedList a = DuplicatedList [a]

duplicate :: [a] -> DuplicatedList a
removeDuplicates :: Eq a => DuplicatedList a -> [a]
-- implementations omitted

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

但是,如果您不导出removeDuplicates,我认为在这里使用非详尽模式是完全可以的。一旦您导出它,您将失去对输入的控制,并且必须处理丢失的案例!

于 2010-09-27T14:39:24.963 回答