可变参数如何实现?我们需要某种机制来表示参数列表的结尾。这可以是
- 特殊的终止符值,或
- 作为额外参数传递的vararg列表的长度。
这两种机制都可以在currying的上下文中使用,以实现varargs,但是正确的键入成为主要问题。假设我们正在处理一个函数sum: ...int -> int
,除了该函数使用currying(所以我们实际上有一个更像的类型sum: int -> ... -> int -> int
,只是我们不知道参数的数量)。
情况:终止符值:end
设为特殊终止符,并T
为类型sum
。现在我们知道应用于end
该函数的返回值是:sum: end -> int
,而应用于int的返回值是另一个类似于sum的函数:sum: int -> T
。因此T
是以下类型的联合:T = (end -> int) | (int -> T)
。通过置换T
,我们得到各种可能的类型,如end -> int
,int -> end -> int
,int -> int -> end -> int
,等。然而,大多数类型的系统没有适应这种类型。
案例:显式长度:vararg函数的第一个参数是varargs的数量。所以sum 0 : int
,sum 1 : int -> int
,sum 3 : int -> int -> int -> int
等,这是在一些类型的系统中支持并是一个例子依赖打字。实际上,参数个数将是一个类型参数,而不是一个常规的参数-它是没有意义的功能的元数依赖于运行时的值,s = ((sum (floor (rand 3))) 1) 2
显然是生病类型的:这个计算结果为s = ((sum 0) 1) 2 = (0 1) 2
,s = ((sum 1) 1) 2 = 1 2
或s = ((sum 2) 1) 2 = 3
。
实际上,这些技术都不应该使用,因为它们容易出错,并且在普通类型系统中没有(有意义的)类型。相反,只需将值列表作为一个参数传递:sum: [int] -> int
。
是的,对象有可能同时显示为函数和值,例如在带有强制的类型系统中。让sum
是SumObj
,它有两个强制转换:
coerce: SumObj -> int -> SumObj
允许sum
用作功能,并且
coerce: SumObj -> int
允许我们提取结果。
从技术上讲,这是上述终止符值情况的变体,带有T = SumObj
,并且coerce
是该类型的解包器。在许多面向对象的语言中,这可以通过操作符重载轻松实现,例如C ++:
#include <iostream>
using namespace std;
class sum {
int value;
public:
explicit sum() : sum(0) {}
explicit sum(int x) : value(x) {}
sum operator()(int x) const { return sum(value + x); } // function call overload
operator int() const { return value; } // integer cast overload
};
int main() {
int zero = sum();
cout << "zero sum as int: " << zero << '\n';
int someSum = sum(1)(2)(4);
cout << "some sum as int: " << someSum << '\n';
}