The D Programming Language/Seq
"Built-in Tuple" FAQ
- Q: What, if anything, is a "built-in tuple"?
"Built-in tuple" is a fancy name given to template argument lists.
- Q: How do I construct it?
Construct it by instantiating a template.
Foo!(1,int,bar,"as",df,double) ^~~~~~~~~~~~~~~~~~~~~~~~~~ ^ A template argument list / built-in tuple
- Q: How do I destruct it?
Specify a pattern within a template parameter list:
template Foo(int x, T, alias y, S...){ ... }
This will match an argument list whose first element is some integer, the second element is some type, the third element is not a built-in type. (The last point is a random DMD quirk that is easier to carry over to the spec than to fix within the compiler. "It is not a symbol!") Remaining arguments are collected into S, which itself is a template argument list. There can be only one such parameter, and it must be the last one.
Furthermore, it comes with handy projectors for all its constituents:
SomeBuiltInTuple[0] SomeBuiltInTuple[ctfeAbleWhereResultWithinBounds()]
In case the goal is to collect a contiguous subsequence into a new one, the slicing operator is also supported. '$' can be used within index and slice expressions.
- Q: How to use it outside of a template instance?
Alias it so that it is accessible outside, Eg:
template Seq(T...){ alias Seq=T; } alias onetofive = Seq!(1,2,3,4,5);
The first of the above definitions can also be found within Phobos:
import std.typetuple: Seq=TypeTuple;
(Protip: This more verbose than defining it yourself.)
- Q: What's the deal with "auto-expansion"?
Argument lists expand into an arbitrary subset of comma-separated contexts. DMD supports at least:
- Function argument list (including struct constructor calls, excluding arguments to overloaded operators):
foo(Seq!(1,2),3); // meaning: foo(1,2,3)
- Template argument list (this is why you cannot have argument lists nested within argument lists).
Foo!(0,Seq!(1,2)) // meaning: Foo!(0,1,2)
- Index argument list:
a[Seq!(1)] // meaning: a[1]
- new argument list(s):
new A(Seq!(1,2)); // meaning new A(1,2) // new(Seq!(1,2)) A(Seq!(1,2)); // to be deprecated
- Parent list:
class A: Seq!(B,C){ } // meaning: class A: B,C{ } class B: Seq!(){ } // meaning: class B{ }
- Array literal element list:
[0,Seq!(1,2),3] // meaning: [0,1,2,3]
- Q: Why doesn't auto-expansion work in context X? (Where X could be Eg. overloaded operator arguments, assert arguments, mixin argument, case list, template parameter list, ...)
No reason.
- Q: Can I nest those?
No. See above. You could, however, use a non-eponymous version of Seq to pack one into an aliasable scope.
template Seq(T...){ alias Seq=T; } template Pack(T...){ alias Seq=T; } pragma(msg, Seq!(Pack!(int,double),Pack!(double,int))[0].Seq[1]); // double
- Q: What's this std.typecons.Tuple then?
D conflates lists of types with types of lists. This means that Eg:
static assert(is(typeof(Seq!(1,2,3))==Seq!(int,int,int));
Furthermore, this implies that one can declare variables/fields/parameters of such a type:
Seq!(int,int,int) x;
This is treated like:
int _x_field_0,_x_field_1,_x_field_2; alias x=Seq!(_x_field_0,_x_field_1,_x_field_2);
Except that the field names are not inserted into the current scope. Note that if x is a parameter, this creates multiple parameters:
int plus(Seq!(int,int) x){ return x[0]+x[1]; } assert(1.plus(2)==3);
std.typecons.Tuple is, modulo a convoluted implementation of named fields, a convoluted version of the following:
struct Tuple(T...){ T expand; alias expand this; this()(T a){expand=a;} this(TT)(Tuple!TT rhs){this(rhs.expand);} } auto tuple(T...)(T args){ return Tuple(args); }
The alias this forwards assignment, static indexing and slicing to 'expand', since there is no way to overload those operations. Hence, tuple(1,2)[] is auto-expanding by accident. Alias this does not, however, forward auto-expanding directly. (This means you can have std.typecons.Tuple s of std.typecons.Tuple s).
- Q: Can I return an argument list from a function?/Does D support multiple return values?
Eg:
Seq!(int, int) id2(int x, int y){ return Seq!(x,y); } auto seq(T...)(T args){ return args; } // usage: void foo(int a, int b, int x, int y){ return (a-x)*(b-y)+(b-x)*(a-y); } seq(1,2).foo(id2(3,4));
DMD does not support multiple return values.
- Q: Can I create an array with multiple element types?
Eg:
Seq!(int,int)[] = [ // uh oh.
No.
- Q: What did you forget to tell me?
Terminology. Argument lists that are also types are sometimes referred to as "type tuples". The others are sometimes referred to as "expression tuples".