如何在 Hindley-Milner 中表示具有多个参数的函数?
How to represent functions with multiple arguments in Hindley-Milner?
我正在阅读一个名为 Professor Frisby's Mostly Adequate Guide to Functional Programming 的函数式编程教程,作者介绍了 Hindley-Milner
和几个关于它的例子,其中之一是:
// reduce :: (b -> a -> b) -> b -> [a] -> b
var reduce = curry(function(f, x, xs) {
return xs.reduce(f, x);
});
reduce
的第一个参数是一个函数,其类型签名是b -> a -> b
,而这正是我不明白的部分。上面的代码是用js写的,也就是说f
要带两个参数,return要带一个参数,像这样:
function f(b, a) {
return b + a;
}
因此 f
的类型签名应该是 (b, a) -> b
而不是 b -> a -> b
,对吗? f
不应该是一阶函数(由 b -> a -> b
暗示),至少在 js 中不是。
所以我的问题是,这是教程的错误吗?如果是这样,在 Hindley-Milner 中表示 (b, a) -> b
的正确方法是什么?
我自己完成了本教程,我认为通常假定函数是柯里化的,所以 f
是 b -> a -> b
可能违反直觉但不一定是错误的 AFAIK。 (对我所说的一切持保留态度;我不是专家;)
然而 reduce
签名中 f
本身的括号为 reader 提供了重要线索(至少对 JavaScript reader) f
是一个函数:
reduce :: (b -> a -> b) -> b -> [a] -> b
^^^^^^^^^^^^^ ^ ^^^ ^
1 2 3 4
1: f
2: reduction initial value
3: list to reduce
4: reduction result
由于 f
可以 进行柯里化,因此不能保证您会一次性收到所有参数。当然,在这种特殊情况下(减少函数),大多数人会期望 f
将同时应用于两个参数(累加值和值)。
函数签名仅仅是“意图声明”(至少在 JavaScript 中):
f
接受两个参数。
- 第一个参数必须是
b
类型。
- 第二个参数必须是
a
类型(顺便说一句,可以 与 b
相同)。
- return 值类型是
b
类型。
未定义函数应该做什么。
如果 b
是一个数字,那么 f
的这个定义就可以了 AFAIK:(尽管那将是一个非常无用的归约函数)
const f = b => a => 42
由于 xs
被注释为 [a]
,我们可以预期它是一个常规的 javascript 数组。
因此,.reduce
调用在中reduce
函数可以假定为Array.prototype.reduce
。
此数组方法不采用柯里化缩减函数。所以我会说:是的,类型注释是错误的。
要修复它,
- 按照您的建议将类型注释更改为
// reduce :: ((b, a) -> b) -> b -> [a] -> b
,或者
- 更改实现以处理将柯里化函数传递给
Array.prototype.reduce
:
// reduce :: (b -> a -> b) -> b -> [a] -> b
var reduce = curry(function(f, x, xs) {
return xs.reduce((b, a) => f(b)(a), x);
// ^^^^^^^^^^^^^^^^^
});
我正在阅读一个名为 Professor Frisby's Mostly Adequate Guide to Functional Programming 的函数式编程教程,作者介绍了 Hindley-Milner
和几个关于它的例子,其中之一是:
// reduce :: (b -> a -> b) -> b -> [a] -> b
var reduce = curry(function(f, x, xs) {
return xs.reduce(f, x);
});
reduce
的第一个参数是一个函数,其类型签名是b -> a -> b
,而这正是我不明白的部分。上面的代码是用js写的,也就是说f
要带两个参数,return要带一个参数,像这样:
function f(b, a) {
return b + a;
}
因此 f
的类型签名应该是 (b, a) -> b
而不是 b -> a -> b
,对吗? f
不应该是一阶函数(由 b -> a -> b
暗示),至少在 js 中不是。
所以我的问题是,这是教程的错误吗?如果是这样,在 Hindley-Milner 中表示 (b, a) -> b
的正确方法是什么?
我自己完成了本教程,我认为通常假定函数是柯里化的,所以 f
是 b -> a -> b
可能违反直觉但不一定是错误的 AFAIK。 (对我所说的一切持保留态度;我不是专家;)
然而 reduce
签名中 f
本身的括号为 reader 提供了重要线索(至少对 JavaScript reader) f
是一个函数:
reduce :: (b -> a -> b) -> b -> [a] -> b
^^^^^^^^^^^^^ ^ ^^^ ^
1 2 3 4
1: f
2: reduction initial value
3: list to reduce
4: reduction result
由于 f
可以 进行柯里化,因此不能保证您会一次性收到所有参数。当然,在这种特殊情况下(减少函数),大多数人会期望 f
将同时应用于两个参数(累加值和值)。
函数签名仅仅是“意图声明”(至少在 JavaScript 中):
f
接受两个参数。- 第一个参数必须是
b
类型。 - 第二个参数必须是
a
类型(顺便说一句,可以 与b
相同)。 - return 值类型是
b
类型。
未定义函数应该做什么。
如果 b
是一个数字,那么 f
的这个定义就可以了 AFAIK:(尽管那将是一个非常无用的归约函数)
const f = b => a => 42
由于 xs
被注释为 [a]
,我们可以预期它是一个常规的 javascript 数组。
因此,.reduce
调用在中reduce
函数可以假定为Array.prototype.reduce
。
此数组方法不采用柯里化缩减函数。所以我会说:是的,类型注释是错误的。
要修复它,
- 按照您的建议将类型注释更改为
// reduce :: ((b, a) -> b) -> b -> [a] -> b
,或者 - 更改实现以处理将柯里化函数传递给
Array.prototype.reduce
:
// reduce :: (b -> a -> b) -> b -> [a] -> b
var reduce = curry(function(f, x, xs) {
return xs.reduce((b, a) => f(b)(a), x);
// ^^^^^^^^^^^^^^^^^
});