"true" currying [was: Function Currying]

Reiner Pope reiner.pope at REMOVE.THIS.gmail.com
Wed Nov 15 14:34:39 PST 2006


I've done implemented "true" function currying (according to Bill 
Baxter), which converts, say, the function

   static int plus(int a, int b, int c)

to

   ((int delegate(int)) delegate (int)) delegate(int)
      (I added extra brackets to perhaps make it more readable)

which means you call it like this:

   curried_plus(1)(2)(3) instead of
   plus(1,2,3)

and you can do partial function application on it very easily:

   auto plus2 = curried_plus(2);

Side note: about half the code I've attached is to determine the type of 
the return value. This could be completely avoided if we had type 
inference on return values, so

CurriedTypeImpl!(RetType, NewParams).Type bar(MyParam myParam)

   could become

auto bar(MyParam myParam)

and the CurriedType2 and CurriedTypeImpl templates could disappear. In 
fact, it was with these templates that I spent the most time before they 
worked.

Also, typeid's of function/delegate types are printed wrongly (see the 
comments in the main function). I don't know whether that is because of 
writefln or DMD or what, but it doesn't show the fucntion parameters.


Here's the code:

import std.traits;
import std.stdio;
import std.string;


// For finding the return type
template CurriedTypeImpl(RetType, Params...)
{
     static assert(Params.length > 0);
     static if (Params.length == 1)
     {
         alias RetType delegate(Params[0]) Type;
     }
     else
     {
         alias CurriedTypeImpl!(RetType, Params[1..length]).Type 
delegate(Params[0]) Type;
     }
}

// Also for finding the return type
template CurriedType2(DG, KnownParams...)
{
     alias ParameterTypeTuple!(DG)[KnownParams.length .. length] Params;
     alias ReturnType!(DG) RetType;
     alias CurriedTypeImpl!(RetType, Params).Type Type;
}

// Here's where the currying is actually done
CurriedType2!(DG, KnownParams).Type RealCurry(DG, KnownParams...)(DG dg, 
KnownParams p)
{
     alias ParameterTypeTuple!(dg)[KnownParams.length .. length] Params;
         static assert(Params.length > 0);

     alias ReturnType!(dg) RetType;
     alias Params[1..length] NewParams;
     alias Params[0] MyParam;

     struct Foo
     {
         KnownParams p_m;
         DG dg_m;
         static if (Params.length == 1)
         {
             RetType bar(MyParam myParam)
             {
                 return dg_m(p_m, myParam);
             }
         }
         else
         {
             CurriedTypeImpl!(RetType, NewParams).Type bar(MyParam myParam)
             {
                 return RealCurry(dg_m, p_m, myParam);
             }
         }
     }

     Foo* f = new Foo;
     f.dg_m = dg;
     foreach (i, arg; p)
         f.p_m[i] = arg;
     return &f.bar;
}

void main()
{
     static int plus(int x, int y, int z)
     {
     return x + y + z;
     }

     auto curried_plus = RealCurry(&plus);
     auto plus_two = curried_plus(2);
     auto plus_two_plus_three = plus_two(3);

     writefln(plus_two_plus_three(4)); // 9
     writefln(plus_two(5)(6));         // 13
     writefln(typeid(typeof(curried_plus)));   // Should print 'int 
delegate(int) delegate(int) delegate(int)' but actually prints 'int 
delegate() delegate() delegate()'
     writefln(typeid(typeof(plus_two_plus_three))); // Should print 'int 
delegate(int)' but actually prints 'int delegate()'
     writefln(typeid(typeof(plus))); // Should print 'int(int,int,int)' 
but actually prints 'int()'
}

Cheers,

Reiner



More information about the Digitalmars-d mailing list