Groovy

Jarrett Billingsley kb3ctd2 at yahoo.com
Mon Jan 1 08:59:04 PST 2007


"bls" <killing__Zoe at web.de> wrote in message 
news:enb0r2$51f$1 at digitaldaemon.com...

> Suneido supports Smalltalk style "blocks". Basically, a block is a section
> of code within a function, that can be called like a function, but that
> operates within the context of the function call that created it (i.e.
> shares its local variables).

Nested functions and delegate literals in D do (almost, see below) the same 
thing.  Access to outer locals is a very useful feature indeed.

> Blocks can be used to implement user defined "control constructs". (In
> Smalltalk, all control constructs are implemented with blocks.) For 
> example,
> you could implement your own version of "foreach":
>
> for_each = function (list, block)
> {
> for (i = 0; i < list.Size(); ++i)
> block(list[i])
> }
> list = #(12, 34, 56)
> for_each(list)
> { |x| Print(x) }
> =>  12
> 34
> 56

What's funny about this example is that this is _precisely_ how D handles 
iteration with foreach, except that it's hidden behind loop-like syntax. 
The body of a foreach loop is converted into a nested function and passed as 
a callback to the opApply for the given container.

> Suneido treats a block immediately following a function call as an
> additional argument.

This is a really cool idea that some people (myself included) would like to 
see in D.  The closest thing we have now is:

for_each(list, (x)
{
    writefln(x);
});

> Blocks can also be used to execute sections of code in specific 
> "contexts".
> For example, the Catch function traps exceptions and returns them. (This 
> is
> useful in unit tests to verify that expected exceptions occur.)
>
> catcher = function (block)
> {
> try
> return block()
> catch (x)
> return x
> }
> catcher( { xyz } ) => "unitialized variable: xyz"

Exception catcher(void delegate() dg)
{
    try
    {
        dg();
    }
    catch(Exception e)
    {
        return e;
    }

    return null;
}

...

Exception e = catcher
({
    someCode();
});

> But the interesting part is that a block can outlive the function call 
> that
> created it, and when it does so, it keeps its context (set of local
> variables). For example:
>
> make_counter = function (next)
> { return { next++ } }
> counter = make_counter(10)
> Print(counter())
> Print(counter())
> Print(counter())
> =>  10
> 11
> 12
> In this example, make_counter returns a block. The block returns next++. 
> You
> see this type of code in Lisp / Scheme.

This is called a static closure, and is something D's nested functions don't 
do.  The workaround involves manually creating a context using a struct or 
something, and returning a bound delegate using an instance of that 
aggregate allocated with new:

int delegate() makeCounter(int next)
{
    struct Context
    {
        int next;

        int func()
        {
            return next++;
        }
    }

    Context* ctx = new Context;
    ctx.next = next;
    return &ctx.func;
}

...

auto counter = makeCounter(10);

writefln(counter());
writefln(counter());
writefln(counter());

This is basically what Suneido is doing for you behind the scenes.  It seems 
like a relatively simple feature to implement, but there are many issues to 
consider.  One, when do you allocate the context for the delegate?  When it 
leaves the function?  How do you know that it isn't assigned to somewhere 
else and not returned?  So you allocate it when it's declared, but then you 
lose the ability to access the outer function's copy of the locals, and you 
also lose performance if the delegate never needs to be returned, so you're 
allocating an unnecessary context with every call to the outer function. 
Then there's the problem of nested functions in nested functions accessing 
local variables from more than one level of nesting... etc. etc. etc.

Please don't take this post to mean that I'm shooting down your ideas!  I'm 
just trying to show some of the features of D which are something like what 
you've demonstrated. 





More information about the Digitalmars-d mailing list