Ну, что-то в этом духе должно сработать:
template autocurry(alias what) {
import std.traits;
static if(Parameters!what.length)
auto autocurry(Parameters!what[0] arg) {
alias Remainder = Parameters!what[1 .. $];
auto dg = delegate(Remainder args) {
return what(arg, args);
};
static if(Remainder.length > 1)
return &autocurry!dg;
else
return dg;
}
else
alias autocurry = what;
}
int foo(int a, string b, float c) {
import std.stdio; writeln(a, " ", b, " ", c);
return 42;
}
string test() {
import std.stdio; writeln("called test");
return "no args";
}
void main() {
import std.stdio;
alias lol = autocurry!foo;
writeln(lol(30)("lol")(5.3));
auto partial = lol(20);
partial("wtf")(10.5);
alias t = autocurry!test;
writeln(t());
}
Идея довольно проста: сгенерировать вспомогательную функцию на основе оставшихся аргументов - если они есть, вернуть адреспомощник в качестве делегата, в противном случае просто верните делегат, который вызывает собранные аргументы.Небольшая рекурсивность обрабатывает 1+ аргументов arg, а статическое if снаружи обрабатывает 0 аргументов arg, просто возвращая исходную функцию.
Особенности языка, которые нужно отметить:
одноименные шаблоны.Когда шаблон имеет элемент с тем же именем, что и шаблон (в данном случае, autocurry), на него автоматически ссылаются при использовании.
расширение кортежа.Когда я вызываю what(arg, args)
, args
, являющийся встроенным кортежем, автоматически расширяется для создания полного списка аргументов.
различные auto возвращает здесь (явноеauto autocurry
и неявное ключевое слово delegate
без указания типа возвращаемого значения) просто переслать любой другой случайный тип, возвращаемый телом.
В основной функции я сделал alias lol = autocurry!foo;
(Я часто использую lol в качестве имени-заполнителя, lol).Вы также можете перегрузить его на верхнем уровне:
int foo(int a, string b, float c) {
import std.stdio; writeln(a, " ", b, " ", c);
return 42;
}
alias foo = autocurry!foo; // overloads the auto-curried foo with the original foo
И теперь вы можете использовать его напрямую, наряду с оригиналом:
void main() {
foo(30)("lol")(5.3); // overload resolves to curried version
foo(40, "cool", 103.4); // overload resolves to original automatically
}
Если вы предпочитаетеновое имя или перегрузка на ваше усмотрение, либо может работать.
Обратите внимание, что каждый аргумент обязан выделить некоторую память для хранения ее для следующего делегата.ГХ будет нести ответственность за его очистку.