On Wed, Jan 08, 2014 at 08:32:15AM +0100, Jacob Carlborg wrote:
> On 2014-01-07 21:44, H. S. Teoh wrote:
[...]
> >I like the alias idea, so here's the revised proposal:
> >
> >1) Argumentless trailing-delegate syntax:
> >
> >     // Given this declaration:
> >     void foo(alias dg)();
> >
> >     // We can write this:
> >     foo {
> >             // body
> >     }
> >
> >     // which will get translated into:
> >     foo!({ /* body */ });
> >
> >2) With arguments:
> >
> >     // Given this declaration:
> >     void foo(alias dg, A...)(A args);
> >
> >     // Or its non-template equivalent:
> >     void foo(alias dg)(A arg1, B arg2, C arg3, ...);
> >
> >     // We can write this:
> >     foo(a,b,c,...) {
> >             // body
> >     }
> >
> >     // which gets translated into:
> >     foo!({ /* body */})(a,b,c,...);
> >
> >3) With indexing arguments:
> >
> >     // Given this declaration:
> >     void foo(alias dg, I..., A...)(A args)
> >             if (is(typeof(dg(I))));
> >
> >     // Or its non-template equivalent:
> >     void foo(alias dg)(A arg1, B arg2, C arg3, ...) {
> >             ...
> >             dg(i, j, k);
> >             ...
> >     }
> >
> >     // We can write this:
> >     foo(i,j,k,... ; a,b,c,...) {
> >             // body
> >     }
> 
> I would prefer to have the delegate arguments last.

The reason I wrote it this way is so that it parallels the foreach
construction better:

        my_foreach (i; range) {
                ...
        }

parallels:

        foreach (i; range) {
                ...
        }


[...]
> >EXAMPLE:
> >
> >     void for_every_other(alias loopBody, R)(R range)
> >             if (is(typeof(loopBody(ElementType!R.init))))
> >     {
> >             while (!range.empty) {
> >                     loopBody(range.front);
> >                     range.popFront();
> >                     if (!range.empty)
> >                             range.popFront();
> >             }
> >     }
> >
> >     // Prints:
> >     // ---
> >     // 1
> >     // 3
> >     // 5
> >     // ---
> >     for_every_other (i; [1,2,3,4,5,6]) {
> >             writeln(i);
> >     }
> 
> If we instead have the delegate argument last UFCS still works:
> 
> [1,2,3,4,5,6].for_every_other(i) {
>     writeln(i);
> }
> 
> Hmm. Actually, your example is more D like. I don't know which I
> example I like best.
[...]

Keep in mind that the identifier list before the ';' is actually the
delegate's parameter list, it's not passing anything in. They are
placeholders for what the function will pass to the delegate. So:

        my_foreach (i,j ; range) {
                writeln(i + j);
        }

actually means:

        my_foreach(range, (i,j) => writeln(i + j));

and my_foreach could be implemented something like this:

        void my_foreach(alias dg, R)(R range)
                if (is(typeof(dg(size_t.init, ElementType!R.init))))
        {
                size_t idx = 0;
                while (!range.empty) {
                        // N.B.: calls dg with i = idx, j = range.front
                        dg(idx, range.front);

                        range.popFront();
                        idx++;
                }
        }


If we go by this, then UFCS should still work:

        range.my_foreach(i,j) { /* body */ }

should be translated to:

        my_foreach(i, j ; range) { /* body */ }

which in turn translates to:

        my_foreach!((i, j) { /* body */ })(range);

In the first case, there is no ambiguity with `range.my_foreach(i,j);`,
which should translate to `my_foreach(range,i,j);`, because the presence
of the trailing code block without an intervening ';' makes it clear
that the above is intended, rather than `my_foreach(range,i,j);`.

In fact, we can already almost get the desired syntax in the current
language:

        /* Current D already supports this: */
        range.my_foreach!((i,j) {
                /* body */
        });

which isn't that much different from the proposed syntactic sugar:

        range.my_foreach(i,j) {
                /* body */
        }

We're just saving on the '!', ';', and an extra pair of parentheses.

I guess the only real advantage is that we get to imitate built-in
foreach syntax. E.g., if we use the form with arguments but no indexing
arguments, we can pretend to be an if-statement:

        // (Whatever "dynamic if" means...)
        void dynamic_if(alias dg)(bool cond)
                if (is(typeof(dg())))
        {
                // Haha, we're just wrapping the built-in 'if' cuz we
                // can.
                if (cond) dg();
        }

        int x;
        dynamic_if (x==0) {
                writeln("Boo yah!");
        }

Or if we use the argumentless form to implement custom block constructs:

        void pure_block(alias dg)() pure
                if (is(typeof(dg())))
        {
                dg();
        }

        void nothrow_block(alias dg)() nothrow
                if (is(typeof(dg())))
        {
                dg();
        }

        void safe_block(alias dg)() @safe
                if (is(typeof(dg())))
        {
                dg();
        }

        void main() {
                pure_block {
                        // Whoopie! now we acquired a construct for
                        // marking blocks of code pure!
                }

                nothrow_block {
                        // And we can have multiple such blocks in a
                        // single function.
                }

                safe_block {
                        // Now I'm just showing off. :P
                }
        }


T

-- 
Curiosity kills the cat. Moral: don't be the cat.

Reply via email to