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