Haskell不可拒绝模式匹配

12
为了在Haskell路径上获得一个良好的开端,我选择了其创建者Hudak的一本书。因此,我正在学习《Haskell入门》。
我试图理解以下语句,但卡住了:
“从技术上讲,形式参数也是模式,但它们永远不会匹配失败。”
通过我的一点经验,比如对C这样的非函数语言的相对较大熟悉,我可以确定形式参数是函数定义中的参数。所以,如果在C中有一个如下的函数:
int func_add(int a, int d) 

如果我没记错的话,那么传递字符串等其他类型的值将在模式匹配中失败。因此,调用 func_add 时传递 func_add("trs", 5) 是一种模式不匹配的情况。

类似的情况在 Haskell 中也可能发生,当代码通过传递不同类型的参数来调用函数时,很可能会出现错误的理解或解释。

那么,为什么说在 Haskell 中形式参数是不可辩驳的模式匹配呢?


1
在某种意义上,你可以概括为“变量是不可反驳的,字面常量是可反驳的,组合如果任何一部分是可反驳的,则整个组合都是可反驳的”。对于只有一个构造函数(例如元组)的类型,像 (x, y) 这样看似可反驳的模式(或者,它可以被重写为 (,) x y,一个可反驳的字面常量后跟两个不可反驳的变量)可以被视为不可反驳的,因为经过类型检查后,只有(,) 创建的值是合法的。 - chepner
因此,形式参数是不可否认的,因为它是一个变量。 - chepner
3个回答

10

您描述的不是一种"模式",而是一种"类型"。Haskell 也有类型,并且这些类型在编译时解析。每种类型可以有几个模式。例如,列表被定义为:

data Color = Red | Green | Blue | Other String

现在我们可以定义一个名为foo的函数:

foo :: Color -> String
foo <b>Red</b> = "Red"
foo <b>Green</b> = "Green"
foo <b>Blue</b> = "Blue"
foo <b>(Other s)</b> = s

粗体字中的元素都是模式。但这些并非不可反驳:第一个将检查我们是否给予函数一个Red,第二个则检查我们是否给了它Green,第三个检查值是否为Blue,最后我们有一个模式(Other s),它将与所有Other模式匹配(无论s的值是什么),因为s是一个变量,而变量是一个不可反驳的模式:我们不对字符串的值进行任何检查。

请注意,这些检查是在运行时进行的:然而,如果我们调用foo"Red",我们将在编译时得到类型错误,因为Haskell编译器知道foo的类型是Color -> String

如果我们写成:

foo :: Color -> String
foo <b>c</b> = "Some color"
foo Red = "Red"

c是一个不可反驳的模式:它将匹配任何Color对象,因此第二行foo Red永远不会匹配,因此c是一个不可反驳的模式。


@Wilhem: 简而言之,类型检查是在编译时完成的。因此,当使用数据构造函数中列出的值调用“foo”时,将进行验证。 - Agnivesh Singh
@Wilhem:而模式匹配被用来评估函数的结果。但是我认为我没有理解形式参数不可反驳的部分。你能否再详细解释一下? - Agnivesh Singh
一个像 c 这样的变量是不可否认的,因为它匹配 所有可能 满足类型约束的输入。 - Willem Van Onsem
@Wilhelm:好的。我可以得出结论,“c”是这里的形式参数吗? - Agnivesh Singh
@AgniveshSingh:是的。 - Willem Van Onsem
@Wilhem:非常感谢。 - Agnivesh Singh

7

不,传递其他类型的值并不是模式匹配中的失败。这是一种类型错误,代码甚至无法编译。形式参数是“良好类型化程序”的不可否认的模式,而编译器只允许你运行这种类型的程序。


只有在函数式语言(或者是强类型或严格类型的语言)中才会出现编译错误,对吗? - Agnivesh Singh
我猜在“c”中,编译器不会检查这个。 - Agnivesh Singh

3
在Haskell中,您可以以多种方式定义类型。其中一种是引入一个“sum type”,示例如下:
data FooBar = Foo Int | Bar Bool

您可以尝试编写像这样的函数,使用模式匹配:
myFunction (Foo x) = x

但是,那将是一个部分匹配的函数,如果你尝试使用myFunction (Bar False)调用它,你会得到一个异常。

另一方面,你也可以定义单例和类型,像这样:

data MyInt = MyInt Int

这里,您可以编写这样的函数:
myFunction' (MyInt x) = x

在这里,您仍然使用模式匹配,但由于只有一个情况,因此这是完全匹配。如果调用代码编译,则匹配不会失败。
上面的MyInt实际上只是Int的包装器,因此您可以说,如果编写一个函数,该函数接受Int,就像这样,这是相同类型的模式匹配:
myFunction'' :: Int -> Int
myFunction'' x = x + 42

虽然 Int 没有一个值构造器可以用于模式匹配,但是 x 是一个模式,总是匹配一个 Int 值。因此,您可以说函数参数是一个总是成功的匹配。


Seeman:非常感谢,但我认为在完全理解你的答案之前,我需要更多地了解类型构造函数和数据构造函数。 - Agnivesh Singh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接