Как можно реализовать varargs? Нам нужен какой-то механизм для оповещения об окончании списка аргументов. Это может быть
- специальное значение терминатора, или
- длина списка vararg, переданного в качестве дополнительного параметра.
Оба эти механизма могут использоваться в контексте каррирования для реализации varargs, но правильная типизация становится серьезной проблемой. Давайте предположим, что мы имеем дело с функцией sum: ...int -> int
, за исключением того, что эта функция использует каррирование (поэтому у нас на самом деле есть тип более похожий sum: int -> ... -> int -> int
, за исключением того, что мы не знаем количество аргументов).
Случай: значение терминатора: Позвольте end
быть специальным терминатором, и T
будет тип sum
. Теперь мы знаем , что применительно к end
функции возвращает: sum: end -> int
и, применяемые к междунар мы получаем другую сумму-как функция: 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';
}