【问题标题】:A Haskell function is higher order if and only if its type has more than one arrow?Haskell 函数是高阶的当且仅当它的类型有多个箭头?
【发布时间】:2019-09-11 21:09:54
【问题描述】:

一位教授我正在上课的课程的教授声称以下内容。

高阶函数在检查其类型时只能有一个箭头。

我不同意我试图证明它是错误的这种说法。我试图设置一些函数,但后来我发现我的函数可能不是高阶函数。这是我所拥有的:

f x y z = x + y + z
f :: a -> a->  a -> a

g = f 3
g :: a -> a -> a

h = g 5
h :: a -> a

归根结底,我认为我的证明是错误的,但我仍然不相信高阶函数在检查类型时只能有多个箭头。

那么,是否有任何资源或者有人可以证明高阶函数可能只有一个箭头?

【问题讨论】:

  • 高阶函数要么将另一个函数作为参数,要么返回另一个函数。所以如果我们接受a 只能指定一个非函数类型(这不一定是真的)那么你的教授是对的。

标签: haskell functional-programming higher-order-functions


【解决方案1】:

严格来说,这个说法是正确的。这是因为术语“高阶函数”的通常定义(此处取自 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*。如果ab 是一个函数,那当然不适用。通常,该函数的类型将包含一个箭头,我们在这里默认ab 都不包含箭头。嗯,Haskell 有类型同义词,所以我们可以很容易地定义,比如:

type MyFunctionType = Int -> Int

然后一个带有签名 MyFunctionType -> aa -> MyFunctionType 的函数肯定是一个 HOF,即使从签名看它并不“看起来像一个”。

*这里要清楚,ab 指的是尚未指定的特定类型 - 我不是指实际签名 a -> b 这意味着适用于 any 的多态函数 类型 ab,不一定是函数。

【讨论】:

  • 如果f :: a -> b,那么即使ab 可以在f 的特定调用中实例化为函数类型,f 的实现也不能使用事实上,它正在对该调用的函数进行操作。所以我不会仅仅因为它可以被实例化以在函数上工作,我就不会仅仅因为它可以被实例化以在字符串上工作而将它称为字符串函数,而是仅仅因为它可以被实例化为更高阶的多态函数。我们静态地知道它不能对这些类型的值做任何“功能性”或“字符串性”的事情。
  • 同意@Ben,也许我误导了 - 我在最后一节中使用的类型变量,如 ab 不应该指代多态函数,但我想到了ab 更像是“占位符”,将被特定类型替换。如果该类型恰好是函数类型,即使是由于类型同义词而“显然”不是函数类型,那么我是说我们仍然会有一个 HOF,尽管只有一个明确的箭头在类型签名中。
  • 哦,是的,我明白你现在在说什么,这是一个重要的澄清。抱歉我误会了。我有时会为占位符使用大写的标识符以避免与多态变量混淆,但我在谈论特定类型时会混淆。
  • 完全没问题,我应该更加小心,因为通常在 Haskell 中,像 a -> b 这样的东西确实意味着多态类型。我会尝试编辑以使其更清晰,但无论哪种方式,我认为这是一个有用的讨论/澄清,所以感谢提出它!
  • Tiny nitpick:Int -> Proxy (Int -> Int) 是一个有两个箭头的函数类型,但它不是高阶函数的类型。
【解决方案2】:

你的函数是高阶的。确实,以您的功能为例:

f :: a -> a -> a -> a
f x y z = x + y + z

这是一种不太冗长的形式:

f :: a -> (a -> (a -> a))

所以它是一个接受a返回一个函数的函数。高阶函数是(a)将函数作为参数,或(b)返回函数的函数。两者都可以同时为真。这里你的函数f 返回一个函数。

因此,函数始终具有 a -> b 类型,a 是输入类型,b 是返回类型。如果a 有一个箭头(如(c -> d) -> b),那么它是一个高阶函数,因为它需要一个函数作为参数。

如果b 有一个箭头,比如a -> (c -> d),那么这也是一个高阶函数,因为它返回一个函数。

【讨论】:

    【解决方案3】:

    是的,因为 Haskell 函数总是被柯里化,我可以想出一些高阶函数和例子的最小例子:

    1) 至少以函数为参数的函数,如:

    apply :: (a -> b) -> a -> b
    apply f x = f x
    

    2) 至少 3 个参数:

    sum3 :: Int -> Int -> Int
    sum3 a b c = a + b + c
    

    所以可以读作:

    sum3 :: Int -> (Int -> Int)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多