www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Request for Comments: a "curry expression" Feature

reply Russell Lewis <webmaster villagersonline.com> writes:
I mentioned this in .announce, where the 1 response was positive...I 
thought I'd post it here to see if I could draw more discussion 
(positive or negative, I'm interested in both sides).

The idea is a "curry expression", which is an expression inside a 
delegate literal whose value is computed at the time when the delegate 
is created, and the computed value is then passed as a curried parameter 
to the delegate.  (In D, currying is where you declare a hidden struct 
on the heap which carries within it some arguments to be passed to a 
delegate.  When you call the delegate, the curry mechanism passes *both* 
the curried arguments and your call-time arguments (if any) to the 
underlying function.  http://en.wikipedia.org/wiki/Currying)

Here is a basic example:
   int delegate() foo(int i)
     return delegate int() { return curry i; }
The above is syntax sugar for the much less readable:
   int delegate() foo(int i)
     return Curry(i,
                  delegate int(int temp) { return temp; });
where Curry(...) is a template which curries one or more arguments into 
a delegate.

The point here is that this mechanism allows you to "copy" the stack 
frame (or, at least, as much of the stack frame as you need) into a 
delegate which will still be runnable after the stack returns.


Interesting things happen if you curry pointers:
   void delegate(int) SetThisLater(int *ptr)
     return delegate void(int i) { *(curry ptr) = i; };

I don't see any reason why you couldn't have more complex expressions in 
a curry expression:
   real delegate(real) StrangeFunc(real r1)
     // this calls SomeComplexFunc(r1) once (at delegate creation time)
     // and then curries the result
     return delegate real(real r2)
                { return r2+ curry SomeComplexFunc(r1); };
   real delegate(real) StrangeFunc2(real r1)
     // this calls SomeComplexFunc each time that the delegate is called
     return delegate real(real r2)
                { return r2+ SomeComplexFunc(curry r1); };

Sometimes, you want to keep a "running tally" of the results from many 
different calls to the same delegate.  Typically, you would do this 
simply by updating a stack variable.  But that doesn't work if you need 
to keep the delegate alive after the stack returns.  You can do it this way:
   int delegate(int) RunningSum()
     return delegate(int i)
              // "new int" is run at delegate creation time.  So each
              // time that you call this delegate, sum is set to the
              // *same* pointer to the *same* int on the heap.
              int *sum = curry new int;
              (*sum) += i;
              return *sum;

Of course, you can still access your stack variables, if you want.  You 
can even access variables in some expressions, even if they were curried 
into the delegate in other places:
   void Repeat(int count, void delegate() callback)
     for(int i=0; i<count; i++)
   void MyFunc()
     int i = 1;
     Repeat(10, delegate void()
                  // the "curry i" below always uses the curried value
                  // the "i" below reads from the stack frame
                  writefln("%d %d", curry i, i);

                  // this modifies the variable in the stack frame
The code above would print:
   1 1
   1 2
   1 3
   1 4
   1 5
   1 6
   1 7
   1 8
   1 9
   1 10

Oct 05 2007
parent =?ISO-8859-1?Q?Julio_C=E9sar_Carrascal_Urquijo?= writes:
Russell Lewis wrote:
   int delegate() foo(int i)
     return delegate int() { return curry i; }
Yes please! Though real closures would provide this functionality automatically this seams like a nice compromise that might be a lot easier to implement in the compiler.
Oct 05 2007