标准ML:不确定高阶函数的类型

bcl*_*man 0 functional-programming sml

我正在阅读Harper的书,标准ML简介,并且在第11.3节"返回函数"中有点困惑.

1)他定义了一个创建常数函数的函数.他写:

"给定一个值k,应用程序constantly k产生一个函数,k无论何时应用它都会产生.这是一个定义constantly:

val constantly = fn k => (fn a => k)
Run Code Online (Sandbox Code Playgroud)

函数经常有类型'a -> ('b -> 'a)."这对我来说很有意义:你提供一个'a'类型的值,它返回一个总是返回该值(类型为'a)的函数,无论输入是什么(类型'b,可能是或者可能与'a)类型不同.

但他说我们也可以将此功能定义为:

fun constantly k a = k
Run Code Online (Sandbox Code Playgroud)

这看起来像一个函数,它接受两个参数并返回第一个...但不是一个返回另一个函数的函数...

我错过了什么?

2)稍后,哈珀讨论了一个地图功能.我理解基本的地图功能.然后他讨论了一个map函数,它允许我们将传入的函数应用于许多列表(而不是使用相同的传入函数调用我们的原始映射函数多次).他写:

fun map' f nil = nil
    | map' f (h::t) = (f h) :: (map' f t)
Run Code Online (Sandbox Code Playgroud)

"如此定义的函数映射具有类型('a -> 'b) -> 'a list -> 'b list.它采用类型'a - >'b的函数作为参数,并产生另一个类型的函数'a list -> 'b list作为结果."

我在这里很丢失,因为看起来map'只是将f应用于列表中的所有元素并返回结果列表.所以我原以为它会是这样的类型:

('a -> 'b) * 'a list -> 'b list
Run Code Online (Sandbox Code Playgroud)

我哪里错了?

谢谢你的帮助,bclayman

sea*_*mcl 5

你的两个问题都源于对一个函数的实际参数缺乏明确性.在SML中,每个函数(回想一下函数是值)只需要一个参数.该参数可能是一个元组(类型'a * 'b但它仍然是一个参数(需要被解构).

在SML中,fun f x1 x2 ... = T是语法糖val rec f = fn x1 => fn x2 => ... => T.所以constantly k评估为fn a => k.

无论你给地图类型('a -> 'b) * 'a list -> 'b list还是('a -> 'b) -> 'a list -> 'b list图书馆设计问题,但效果是一样的.他们做同样的事情,虽然第一个采用函数和列表的元组,而第二个采用函数优先,并从列表返回一个函数到列表.这在编程语言文献中被称为"currying".(元组版本是"uncurried",另一个是"curried".)


Joh*_*man 5

神奇的词是"currying":https://en.wikipedia.org/wiki/Currying

在ML(以及Haskell)中,函数应用程序比任何其他运算符都更紧密.因此constantly k a解析为(constantly k) a.constantly k绑定到一个函数.要了解什么功能,您可以在心理上考虑定义

fun constantly k a = k
Run Code Online (Sandbox Code Playgroud)

相当于

fun (constantly k) a = k
Run Code Online (Sandbox Code Playgroud)

这表示这(constantly k)是将任何a映射到k的函数.

因此'a -> ('b -> 'a),正如文中所要求的那样.

定义constantlyvia 没有任何违法行为

fun constantly (k,a) = k
Run Code Online (Sandbox Code Playgroud)

它将具有'a * 'b -> 'a您似乎期望的类型,但是"不断地"调用它然后有点奇怪,因为它不是一个常量函数.

我相信哈珀迟早会得到它.ML和Haskell(尤其是Haskell)都大量使用这些函数.