Request for Comments: a "curry expression" Feature
Russell Lewis
webmaster at villagersonline.com
Fri Oct 5 08:28:32 PDT 2007
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?
More information about the Digitalmars-d
mailing list