Haskell中的参数数量和无参函数

15

即使使用点除法,多个模式匹配也不可能具有不同数量的参数!

foo True b = b + 2
foo _ = id

举例来说,它不能正常工作。但是

foo True = (+2)
foo _ = id

有时我们只能在函数的某一部分中使用无点风格(point-free),为什么呢?这是 GHC 处理不了吗? :'(


4
通常认为这种做法不太好。针对一组函数定义,参数数量的不同通常意味着存在错误。 - Thomas M. DuBuisson
6
可以编写良好类型化的函数,其元数根据其参数值而变化。也许这是一项少数人关注的技术,但它使左侧必须具有相同数量的参数这一限制成为一种人为的限制,更不用说是一个麻烦了。左侧不匹配通常是一种错误,但为什么不让类型系统来捕获它们呢? - pigworker
我认为这更方便,而且不会影响可读性。检查类型是否相同,并将 id 翻译为 (\x -> id x) 对编译器来说应该很容易。 - L01man
2
允许不同数量的参数是完全合理的事情。而且编译也很容易。不允许这样做只是一种风格选择。 - augustss
+1 我自己也曾经想过这个问题。 - Dan Burton
参见 http://www.haskell.org/haskellwiki/Pointfree 的 unpl 函数,它可以做到这一点。 - L01man
2个回答

20

为什么? GHC 处理不了吗?

不是的。这对于 GHC 来说一点也不难。实际上,这是 Haskell 报告的问题。

参见:Haskell Report 2010 > 声明和绑定 > 函数绑定

一个函数绑定将变量绑定到一个函数值。变量x的函数绑定的一般形式为:
x p11 … p1k match1 … x pn1 … pnk matchn
[...blah blah...]
翻译:函数的一般绑定形式在语义上等同于方程(即简单模式绑定):
x = \ x1 … xk -> case (x1, …, xk) of (p11, …, p1k) match1 … (pn1, …, pnk) matchn 其中xi是新标识符。
(强调是我的)
虽然函数定义在语义上等同于lambda和case表达式,但它们不一定被编译成这种方式,就像Mihai所建议的那样。
事实上,Haskell报告定义了函数声明,使得它们在等式的左边必须具有相同数量的输入。这一点通过在第一个和第n个函数声明行上保持k不变(暗示着在中间的所有行也是如此)而变得清晰明了。这就是限制的原因;与GHC的实现细节无关。简而言之,不允许这样做只是一种风格选择。-augustss

1
+1 是因为你是唯一一个回答真正问题的人。 - Riccardo T.

4
每个函数方程都必须具有相同数量的参数。这就是为什么你的第一个例子失败的原因。
要修复它,请使用:
foo True b = b + 2
foo _ x = id x

正如您所看到的,这两个方程具有相同数量的参数。

涉及模式匹配的多个方程被翻译为case表达式。在您的情况下,foo 的翻译大致如下:

foo a b = case a of
    True -> b + 2
    _ -> id x

所有的case分支必须具有相同的类型,因此您的第一个示例应该被翻译为:

foo a b = case a of
    True -> b + 2
    _ -> id

这是错误的,因为分支具有不同的类型。

当然,这只是简单地说明问题,背后实际发生的事情更加复杂。


但是在这两种情况下,使用一个参数调用 foo 会完成相同的操作。 - L01man
4
将一个简单函数定义展开成一个带有λ表达式的等式并不能很好地解释问题,更重要的是,将包含“多个”等式的定义转换为带有“多个”case选择的“单个”等式。当这些等式被还原到不同程度时,这种转换并不总是有效的。 - leftaroundabout
1
左侧模式匹配会被展开成case表达式吗? - L01man
@L01man:是的,我认为那就是它的工作原理。 - Tikhon Jelvis
我错了,我在想其他的事情。已经用正确的解释更新了答案(感谢@leftaroundabout指出我的错误)。确实,在核心中,我们只有用于模式匹配的case表达式。 - Mihai Maruseac

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