Ddoc $(D_S Tuples, $(P A tuple is a sequence of elements. Those elements can be types, expressions, or aliases. The number and elements of a tuple are fixed at compile time; they cannot be changed at run time. ) $(P Tuples have characteristics of both structs and arrays. Like structs, the tuple elements can be of different types. Like arrays, the elements can be accessed via indexing. ) $(P So how does one construct a tuple? There isn't a specific tuple literal syntax. But since variadic template parameters create tuples, we can define a template to create one: ) --- template Tuple(E...) { alias E Tuple; } --- $(P and it's used like:) --- Tuple!(int, long, float) // create a tuple of 3 types Tuple!(3, 7, 'c') // create a tuple of 3 expressions Tuple!(int, 8) // create a tuple of a type and an expression --- $(P In order to symbolically refer to a tuple, use an alias:) --- alias Tuple!(float, float, 3) TP; // TP is now a tuple of two floats and 3 --- $(P Tuples can be used as arguments to templates, and if so they are $(SINGLEQUOTE flattened) out into a list of arguments. This makes it straightforward to append a new element to an existing tuple or concatenate tuples:) --- alias Tuple!(TP, 8) TR; // TR is now float,float,3,8 alias Tuple!(TP, TP) TS; // TS is float,float,3,float,float,3 --- $(P Tuples share many characteristics with arrays. For starters, the number of elements in a tuple can be retrieved with the $(B .length) property:) --- TP.length // evaluates to 3 --- $(P Tuples can be indexed:) --- TP[1] f = TP[2]; // f is declared as a float and initialized to 3 --- $(P and even sliced:) --- alias TP[0..length-1] TQ; // TQ is now the same as Tuple!(float, float) --- $(P Yes, $(B length) is defined within the [ ]s. There is one restriction: the indices for indexing and slicing must be evaluatable at compile time.) --- void foo(int i) { TQ[i] x; // error, i is not constant } --- $(P These make it simple to produce the $(SINGLEQUOTE head) and $(SINGLEQUOTE tail) of a tuple. The head is just TP[0], the tail is TP[1 .. length]. Given the head and tail, mix with a little conditional compilation, and we can implement some classic recursive algorithms with templates. For example, this template returns a tuple consisting of the trailing type arguments $(I TL) with the first occurrence of the first type argument $(I T) removed: ) --- template Erase(T, TL...) { static if (TL.length == 0) // 0 length tuple, return self alias TL Erase; else static if (is(T == TL[0])) // match with first in tuple, return tail alias TL[1 .. length] Erase; else // no match, return head concatenated with recursive tail operation alias Tuple!(TL[0], Erase!(T, TL[1 .. length])) Erase; } ---

Type Tuples

$(P If a tuple's elements are solely types, it is called a $(I TypeTuple) (sometimes called a type list). Since function parameter lists are a list of types, a type tuple can be retrieved from them. One way is using an $(ISEXPRESSION): ) --- int foo(int x, long y); ... static if (is(foo P == function)) alias P TP; // TP is now the same as Tuple!(int, long) --- $(P This is generalized in the template $(LINK2 phobos/std_traits.html, std.traits).ParameterTypeTuple: ) --- import std.traits; ... alias ParameterTypeTuple!(foo) TP; // TP is the tuple (int, long) --- $(P $(I TypeTuple)s can be used to declare a function:) --- float bar(TP); // same as float bar(int, long) --- $(P If implicit function template instantiation is being done, the type tuple representing the parameter types can be deduced: ) --- int foo(int x, long y); void Bar(R, P...)(R function(P)) { writefln("return type is ", typeid(R)); writefln("parameter types are ", typeid(P)); } ... Bar(&foo); --- $(P Prints:) $(CONSOLE return type is int parameter types are (int,long) ) $(P Type deduction can be used to create a function that takes an arbitrary number and type of arguments:) --- void Abc(P...)(P p) { writefln("parameter types are ", typeid(P)); } Abc(3, 7L, 6.8); --- $(P Prints:) $(CONSOLE parameter types are (int,long,double) ) $(P For a more comprehensive treatment of this aspect, see $(LINK2 variadic-function-templates.html, Variadic Templates). )

Expression Tuples

$(P If a tuple's elements are solely expressions, it is called an $(I ExpressionTuple). The Tuple template can be used to create one: ) --- alias Tuple!(3, 7L, 6.8) ET; ... writefln(ET); // prints 376.8 writefln(ET[1]); // prints 7 writefln(ET[1..length]); // prints 76.8 --- $(P It can be used to create an array literal:) --- alias Tuple!(3, 7, 6) AT; ... int[] a = [AT]; // same as [3,7,6] --- $(P The data fields of a struct or class can be turned into an expression tuple using the $(B .tupleof) property:) --- struct S { int x; long y; } void foo(int a, long b) { writefln(a, b); } ... S s; s.x = 7; s.y = 8; foo(s.x, s.y); // prints 78 foo(s.tupleof); // prints 78 s.tupleof[1] = 9; s.tupleof[0] = 10; foo(s.tupleof); // prints 109 s.tupleof[2] = 11; // error, no third field of S --- $(P A type tuple can be created from the data fields of a struct using $(B typeof):) --- writefln(typeid(typeof(S.tupleof))); // prints (int,long) --- $(P This is encapsulated in the template $(LINK2 phobos/std_traits.html, std.traits).FieldTypeTuple. )

Looping

$(P While the head-tail style of functional programming works with tuples, it's often more convenient to use a loop. The $(I ForeachStatement) can loop over either $(I TypeTuple)s or $(I ExpressionTuple)s. ) --- alias Tuple!(int, long, float) TL; foreach (i, T; TL) writefln("TL[%d] = ", i, typeid(T)); alias Tuple!(3, 7L, 6.8) ET; foreach (i, E; ET) writefln("ET[%d] = ", i, E); --- $(P Prints:) $(CONSOLE TL[0] = int TL[1] = long TL[2] = float ET[0] = 3 ET[1] = 7 ET[2] = 6.8 )

Tuple Declarations

$(P A variable declared with a $(I TypeTuple) becomes an $(I ExpressionTuple):) --- alias Tuple!(int, long) TL; void foo(TL tl) { writefln(tl, tl[1]); } foo(1, 6L); // prints 166 ---

Putting It All Together

$(P These capabilities can be put together to implement a template that will encapsulate all the arguments to a function, and return a delegate that will call the function with those arguments.) --- import std.stdio; R delegate() CurryAll(Dummy=void, R, U...)(R function(U) dg, U args) { struct Foo { typeof(dg) dg_m; U args_m; R bar() { return dg_m(args_m); } } Foo* f = new Foo; f.dg_m = dg; foreach (i, arg; args) f.args_m[i] = arg; return &f.bar; } R delegate() CurryAll(R, U...)(R delegate(U) dg, U args) { struct Foo { typeof(dg) dg_m; U args_m; R bar() { return dg_m(args_m); } } Foo* f = new Foo; f.dg_m = dg; foreach (i, arg; args) f.args_m[i] = arg; return &f.bar; } void main() { static int plus(int x, int y, int z) { return x + y + z; } auto plus_two = CurryAll(&plus, 2, 3, 4); writefln("%d", plus_two()); assert(plus_two() == 9); int minus(int x, int y, int z) { return x + y + z; } auto minus_two = CurryAll(&minus, 7, 8, 9); writefln("%d", minus_two()); assert(minus_two() == 24); } --- $(P The reason for the $(I Dummy) parameter is that one cannot overload two templates with the same parameter list. So we make them different by giving one a dummy parameter. )

Future Directions

$(UL $(LI Return tuples from functions.) $(LI Use operators on tuples, like =, +=, etc.) $(LI Have tuple properties like $(B .init) which will apply the property to each of the tuple members.) ) ) Macros: TITLE=Tuples WIKI=Tuples META_KEYWORDS=D Programming Language, template metaprogramming, variadic templates, tuples, currying META_DESCRIPTION=Tuples in the D programming language