A Discussion of Tuple Syntax
Michel Fortin
michel.fortin at michelf.ca
Tue Aug 20 19:13:42 PDT 2013
On 2013-08-20 20:16:48 +0000, Andrei Alexandrescu
<SeeWebsiteForEmail at 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.fortin at michelf.ca
http://michelf.ca
More information about the Digitalmars-d
mailing list