View mode: basic / threaded / horizontal-split · Log in · Help
October 05, 2007
Request for Comments: a "curry expression" Feature
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.



LOTS MORE EXAMPLES:

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++)
      callback();
  }
  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
                 i++;
               };
  }
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


Thoughts?
October 05, 2007
Re: Request for Comments: a "curry expression" Feature
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.
Top | Discussion index | About this forum | D home