A currying function
bearophile
bearophileHUGS at lycos.com
Mon Jun 18 13:24:59 PDT 2012
I think std.functional.curry is badly named because I think it's
a "partial application" (I suggest to rename it).
In Haskell and Scala currying is built-in, this is Haskell, shell
(ghci):
Prelude> let foo x y z = x * 2 + y * 3 + y * 5
Prelude> let foo1 = foo 5
Prelude> let foo2 = foo1 4
Prelude> foo2 3
42
Through Reddit I've found a curry() in C++11:
https://github.com/LeszekSwirski/cpp-curry
So I've written a D version of it below, not tested much. I have
not translated the C++11 version. This was quite easy to write,
and much simpler and shorter than the C++11 version (but maybe
this misses something, this is just a first draft). This kind of
stuff was quite longer to do in D1, but now with CTFE on strings
ops, defining one or more inner static functions that build a
string at compile-time, it's quite easy. Now I don't need lot of
templates to do this.
There are two alternative designs, curry1 seems a bit more
elegant, but if you want to use it in-place it forces you to add
a () at the beginning, that's not nice and intuitive. So probably
curry2 is better.
import std.traits: isCallable, ParameterTypeTuple;
import std.string: join;
import std.conv: xformat;
@property curry1(alias F)() if (isCallable!F) {
static string genChain() {
string[] chain;
string[] args;
foreach (i, T; ParameterTypeTuple!F) {
chain ~= xformat("(%s x%d)", T.stringof, i);
args ~= xformat("x%d", i);
}
return chain.join(" => ") ~ " => F(" ~ args.join(", ") ~
");";
}
mixin("return " ~ genChain());
}
auto curry2(F)(F f) if (isCallable!F) {
static string genChain() {
string[] chain;
string[] args;
foreach (i, T; ParameterTypeTuple!F) {
chain ~= xformat("(%s x%d)", T.stringof, i);
args ~= xformat("x%d", i);
}
return chain.join(" => ") ~ " => f(" ~ args.join(", ") ~
");";
}
mixin("return " ~ genChain());
}
// default arguments are ignored
double foo(immutable int x, in float y, short z=5) pure nothrow {
return x * 2 + y * 3 + y * 5;
}
void main() {
import std.stdio;
writeln(foo(5, 4, 3));
auto cf = curry1!foo;
auto c2 = cf(5)(4);
writeln(c2(3));
writeln(curry1!foo()(5)(4)(3));
writeln(curry2(&foo)(5)(4)(3));
}
This is the code string generated by genChain() for the foo()
function:
(immutable(int) x0) => (const(float) x1) => (short x2) => f(x0,
x1, x2);
I think this code induces the creation of heap allocated
closures, so probably a more efficient version is needed:
_D4test24__T5curryTPFNaNbyixfs...
L0: push EAX
push EAX
push EBX
push 8
call near ptr __d_allocmemory
mov EBX,EAX
mov EAX,0Ch[ESP]
mov [EBX],EAX
fld float ptr 014h[ESP]
mov EAX,EBX
mov EDX,offset FLAT:_D4test24__T5curryTPFNaNbyixf...
fstp float ptr 4[EBX]
add ESP,4
pop EBX
add ESP,8
ret 4
If the function is:
double bar(ref int x, ref double y) pure nothrow {
x++;
return x * 2 + y * 3;
}
it generates:
(int x0) => (double x1) => f(x0, x1);
So this version doesn't handle ref arguments.
Bye,
bearophile
More information about the Digitalmars-d
mailing list