On 2013-08-20 20:16:48 +0000, Andrei Alexandrescu <seewebsiteforem...@erdani.org> said:

On 8/20/13 6:57 AM, Dicebot wrote:
Preserve
auto-expansion.

That would be problematic to say the least. (There have been a few discussions in this group; I've come to think auto expansion is fail.)

I used to like auto expansion, probably because it was convenient for what I was doing. I always feared that not having auto-expansion would make my code ugly.

But now that I've been working with C++11 with its variadic template parameters that don't auto expand, I'm quite fond of it. To expand a "tuple" all you have to do is suffix it with "...", example:

        template < typename A, typename B, typename... C >
        shared_ptr< Expression > make_exp(Operator op, A first, B second, C... 
more)
        {
                return make_exp(op, make_exp(op, first, second), more...);
        }

Now, if we could get rid of auto expansion while requiring a "..." suffix to expand a tuple, I think it'd be really great. Can't we steal this from C++?

What follows is how I'd transpose this to D.

First, let's put the three-dots *before* the template argument (somewhat as in C++) to get a packed tuple. This will ensure backward compatibility (if you put the three-dots after, like current D, you'd still get an auto-expanding tuple):

        Expression make_exp(A, B, ...C)(Operator op, A first, B second, C... 
more)
        {
                return make_exp(op, make_exp(op, first, second), more...);
        }

Basically, the rule is simple: three dots before an expression means "pack", three dots after it means "expand".

In the example above, the template argument C is a packed tuple type. Expanding C in the function's argument list means that we're expecting arguments to come as separate argument and not as one packed tuple argument. Those separate arguments get packed into the "more" parameter variable, which is of type C, which is then expanded as requested by the three-dot suffix when calling the function.

Extrapolating things a little, a packed tuple type could be expressed like this:

        ...(int, double) x; // a packed type tuple variable
        x = ...(1, 2.3); // create a packed value tuple and assign it to x.
        writeln(x...); // expand tuple to make it two separate arguments
        writeln(x); // keep it packed and you're passing it as one argument

        ...(int, ...(double, float)) y; // nested tuples

Also, you can take an auto-expanding tuple and make it packed:

        template (X...)
        {
                ...(X) x; // auto-expanding tuple becomes packed
        }

Which is the same as making it packed directly from the argument list:

        template (...X)
        {
                X x; // already packed tuple
        }

And since this is a "language-type" tuple, it can contain a mix of expression, types, aliases, etc, and therefore can be used like this:

...(int, "ha!", writeln); // can contain anything usable as a template argument

Other examples:

        int x;
        double y;
        ...(x, y) = ...(1, 2.3); // multiple assignments
        ...(x, y) = getPackedTuple();

And finally, we could support expanding whole expressions that have a packed tuple in them by shifting the expanding three-dot outward, as you can do with C++11:

        ...(int, double) x;
        func(exp(x+5)...); // expands to: func(exp(x[0]+5), exp(x[1]+5));

Wouldn't that be great?

After some time, if desired, auto-expanding tuples could be deprecated. The migration path wouldn't be too hard, nor too verbose: just add those three-dots right and left as needed.

--
Michel Fortin
michel.for...@michelf.ca
http://michelf.ca

Reply via email to