严格来说,这个说法是正确的。这是因为术语“高阶函数”的通常定义(此处取自 Wikipedia)是执行以下一项或两项操作的函数:
很明显,类型签名中没有单个箭头的函数不能是高阶函数,因为在签名a -> b 中,没有“空间”来创建x -> y 形式的东西箭头的任一侧 - 根本没有足够的箭头。
(这个论点实际上有一个重大缺陷,你可能已经发现了,我将在下面说明。但对于你教授的意思,这可能是“精神上的”。)
严格来说,相反的情况在 Haskell 中也是正确的——尽管在大多数其他语言中不是这样。 Haskell 的显着特点是函数是curried。比如(+)这样的函数,其签名为:
a -> a -> a
(我将忽略 Num a 约束,因为如果我们应该计算“箭头”,它可能只会混淆问题),通常被认为是两个参数的函数:它需要 2 as 并产生另一个 a。在大多数语言中,当然都有类似的函数/运算符,这永远不会被描述为高阶函数。但是在 Haskell 中,因为函数是柯里化的,所以上面的签名实际上只是括号版本的简写:
a -> (a -> a)
显然是一个高阶函数。它接受a 并产生a -> a 类型的函数。 (回想一下,返回一个函数是 HOF 的特征之一。)正如我所说,在 Haskell 中,这两个签名是一回事。 (+) 真的 是 一个高阶函数——我们只是经常没有注意到,因为我们打算给它两个参数,我们真正的意思是给它一个参数,结果是一个函数,然后将第二个参数提供给 that 函数。多亏了 Haskell 方便的、无括号的、将函数应用于参数的语法,实际上并没有任何区别。 (这再次与非函数式语言形成对比:那里的加法“函数”总是正好有 2 个参数,只给它一个通常会出错。如果语言有一流的函数,你确实可以定义柯里化形式,例如在 Python 中:
def curried_add(x):
return lambda y: x + y
但这显然与您通常使用的两个参数的直接函数不同,并且应用起来通常不太方便,因为您需要将其称为 curried_add(x)(y) 而不仅仅是说 add(x,y)。
所以,如果我们考虑到柯里化,你教授的说法是完全正确的。
嗯,除了我上面提到的以下例外。我一直在假设带有表单签名的东西
a -> b
不是 HOF*。如果a 或b 是一个函数,那当然不适用。通常,该函数的类型将包含一个箭头,我们在这里默认a 或b 都不包含箭头。嗯,Haskell 有类型同义词,所以我们可以很容易地定义,比如:
type MyFunctionType = Int -> Int
然后一个带有签名 MyFunctionType -> a 或 a -> MyFunctionType 的函数肯定是一个 HOF,即使从签名看它并不“看起来像一个”。
*这里要清楚,a 和 b 指的是尚未指定的特定类型 - 我不是指实际签名 a -> b 这意味着适用于 any 的多态函数 类型 a 和 b,不一定是函数。