The future of lambda delegates
S. Chancellor
dnewsgr at mephit.kicks-ass.org
Wed Aug 16 22:24:07 PDT 2006
On 2006-08-16 07:55:38 -0700, "Mikola Lysenko" <mclysenk at mtu.edu> said:
> D's delegates are great. They are vastly superior to C++'s confused
> pointer-to-member functions, and much more useful than Java's adapter
> classes. In the world of system programs, nothing comes close. One
> interesting type of delegate is the anonymous-nested-delegate-literal,
> or "lambda" delegate. For those unacquainted with such niceties, here
> are the documentation links:
> http://www.digitalmars.com/d/function.html#nested
> http://www.digitalmars.com/d/expression.html#FunctionLiteral
>
> Recent D releases (notably 0.161) have updated the syntax and improved
> the overall usability of lambda delegates. All the syntactic sugar is
> nice, but it also lays a few traps for the unwary. Here is a simple
> example:
>
> // The Fibonacci numbers are an integer sequence such that
> // F(n) = F(n - 1) + F(n - 2)
> // And F(0) = 0, F(1) = 1
> int delegate() fibs()
> {
> int a = 0; // Initialize the last two terms of the
> Fibonacci sequence
> int b = 1;
>
> return
> {
> int c = a + b; // Calculate the next term in the sequence
> a = b; // Shift the previous terms back
> b = c;
> return c; // Return the result
> };
> }
>
> This function returns a function which will sequentially evaluate all
> of the Fibonacci numbers. Notice that the inner delegate modifies the
> variables a and b in fibs() scope. Because of this, it is not
> guaranteed to work after fibs returns. This is most irritating, and it
> greatly restricts the use of this technique. Another potential use for
> lambda delegates is to create one-line adapter methods. Consider the
> following attempt to create a button which will display an arbitrary
> message when clicked:
>
> Button createButton(char[] click_msg)
> {
> Button b = new Button();
> b.mouseClickCallback = { MsgBox(click_msg); };
> return b;
> }
>
> Once more, this sort of method relies on access to the outer function
> scope from within the lambda delegate. Given the current semantics,
> this code is doomed to fail in any number of random ways. As a final
> example, suppose we want to perform a lengthy calculation in a separate
> thread, and only wait for the value once it is needed. In this case,
> one could try to do the following:
>
> int[] delegate() sort_threaded(int[] array)
> {
> int[] result = array.dup;
>
> //Create and run a worker thread to perform an expensive operation,
> (in this case a sort)
> Thread tsort = new Thread(
> {
> result.sort;
> return 0;
> });
> tsort.start();
>
> //The returned lambda delegate waits for the thread's calculation
> to finish, then returns the result.
> return { tsort.wait; return result; };
> }
>
> In this situation, we can let the thread execute in the background
> while the program performs other tasks, and only wait for the result
> once we need it. This type of deferred calculation can be very useful
> for improving the amount of parallelism within an application without
> adding much synchronization overhead. It is very sad that none of
> these examples work, since there are so many nice solutions just like
> them.
>
> One possible solution is to allocate the frame for each function
> containing nested-functions on the heap. This allows any returned
> delegates to use the member variables freely. One could think of it as
> translating the function into a class with a thunk. Here is above
> Fibonacci function rewritten in this way:
>
> class fibs_
> {
> int a = 0;
> int b = 1;
>
> int lambda1()
> {
> int c = a + b;
> a = b;
> b = c;
> return c;
> }
>
> int delegate() func()
> {
> return &lambda1;
> }
> }
>
> int delegate() fibs()
> {
> return (new fibs_()).func();
> }
>
> Such a translation is possible for any of these examples, albeit quite
> tedious. From what I gather C# applies a similar technique with its
> lambda delegates and inner classes. For a machine code compiler, such
> a rewrite is not even necessary, since it could simply overwrite the
> frame pointer at the start of the function with a GC allocated block.
> This would preserve frame-based addressing for arguments and variables,
> requiring no change in any of the internally generated machine code.
> The run time overhead is fairly low, only on the order of one extra
> memory allocation per function call. Practically, such an
> implementation would be extremely simple.
>
> Any thoughts or comments?
>
> -Mikola Lysenko
Perl has this same problem. May be of use to look at how perl solves it.
More information about the Digitalmars-d
mailing list