The D Programming Language/Seq

From D Wiki
Jump to: navigation, search

"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.

   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:


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]; }

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?


   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); }

DMD does not support multiple return values. However, it can be simulated using std.typecons.Tuple E.g.:

   auto foo()
       int v0 = 3;
       double v1 = 6.6;
       return tuple(v0, v1);
   auto a = foo();
   assert(is(typeof(a[0]) == int));
   assert(a[0] == 3);
   assert(is(typeof(a[1]) == double));
   assert(a[1] == 6.6);

  • Q: Can I create an array with multiple element types?


   Seq!(int,int)[] = [ // uh oh.


  • 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".