Skip to content

Commit

Permalink
New documentation for compile-time lists
Browse files Browse the repository at this point in the history
  • Loading branch information
Dicebot committed May 29, 2015
1 parent 39cd533 commit badd766
Show file tree
Hide file tree
Showing 2 changed files with 221 additions and 352 deletions.
217 changes: 217 additions & 0 deletions ctarguments.dd
@@ -0,0 +1,217 @@
Ddoc

$(D_S Compile-time (argument) lists,

$(P Compile-time lists is a very important metaprogramming concept that comes naturally
from D support for $(LINK2 variadic-function-templates.html, variadic templates). Those
allow programmer to operate on types, symbols and expression allowing to define
compile-time algorithms that operate on types, symbols and expressions.
)

$(P For historical reasons those sometimes can be called tuples in documentation or compiler
internals but don't get confused : this doesn't have much in common with tuples that
commonly exist in other languages. Sequences of values of different types that can be
returned from functions are provided by $(LINK2 phobos/std_typecons.html#.Tuple, std.typecons.Tuple).
Using term "tuple" to mean compile-time lists is discouraged to avoid confusion and
noticing one should result in a $(LINK issues.dlang.org, documentation bug report).
)

$(P Consider this simple snippet:)

---
template Variadic(T...) { /* ... */ }
---

$(P `T` here is variadic $(LINK2 template.html#TemplateArgumentList, template argument list)
which is a core language feature. It is has own very special semantics
and from the programmer point of view is most similar to array of compile-time entities - types,
symbols (names) and expressions (values). You can check the length of variadic argument list
and access any individual element:
)

---
template Variadic(T...)
{
static assert (T.length > 1);
pragma(msg, T[0]);
pragma(msg, T[1]);
}

alias Dummy = Variadic!(int, 42);
// prints during compilation:
// int
// 42
---

$(P However, language itself does not provide any means to define such lists outside of
template parameter declaration. Instead, there is a
$(LINK2 phobos/std_meta_arglist.html, simple utility) provided by D standard library:
)

---
alias Arguments(T...) = T;
---

$(P All it does is giving specific variadic argument list externally accessible name so
that it can be worked with in any other context:
)

---
import std.meta.arglist;
// can alias to some other name
alias Name = Arguments!(int, 42);
pragma(msg, Name[0]);
pragma(msg, Name[1]);
// or work with a list directly
pragma(msg, Arguments!("aaa", 0, double)[2]);
---

$(H3 Available operations)

$(H4 Checking the length)

---
import std.meta.arglist;
static assert (Arguments!(1, 2, 3, 4).length == 4);
---

$(H4 Indexing and slicing)

$(P Indexes must be known at compile-time)

---
import std.meta.arglist;
alias Numbers = Arguments!(1, 2, 3, 4);
static assert (Numbers[1] == 2);
alias SubNumbers = Numbers[1 .. $];
static assert (SubNumbers[0] == 2);
---

$(H4 Assignment)

$(P Works only if list element is a symbol that refers to a mutable variable)

---
import std.meta.arglist;

void main()
{
int x;
alias List = Arguments!(10, x);
List[1] = 42;
assert (x == 42);
// List[0] = 42; // won't compile, can't assign to a constant
}
---

$(H4 Loops)

$(P D $(LINK2 dlang.org/statement.html#ForeachStatement, foreach statement) has special
semantics when iterating over compile-time list. It duplicates body of the loop
for each of the list elements, with loop iteration "variable" becoming an alias
for that compile-time list elements.
)

---
import std.meta.arglist;

void main()
{
foreach (sym; Arguments!(int, "literal", main))
{
static if (is(sym))
pragma (msg, sym);
else
pragma (msg, typeof(sym));
}
}

/* Prints:

int
string
void()
*/
---

$(H3 Auto-expansion)

$(P One of less obvious properties of compile-time argument lists is that when used
as an argument to function or template those are automatically treated as list
of comma-separated arguments instead:
)

---
import std.meta.arglist;

template Print0(T...)
{
pragma(msg, T[0]);
}

alias Dummy = Print0!(Arguments!(int, double));
---

$(P This will only print `int` during compilation because last line gets rewritten
as `alias Dummy = Print0!(int, double)`. If auto-expansion didn't happen,
`Arguments!(int, double)` would be printed instead. This is inherent part of
language semantics for variadic lists and thus also preserved by `Arguments`.
)

$(H3 Homogenous lists)

$(P `Arguments` that only consists from only type or expression (value) elements is
commonly called "type list" or "expression list" accordingly. Concept of
"symbol list" is rarely mentioned explicitly but fits the same pattern.
)

$(P It is possible to use homogenous type lists in declarations:)

---
import std.meta.arglist;
alias Params = Arguments!(int, double, string);
void foo(Params); // void foo(int, double, string);
---

$(P D supports special variable declaration syntax where type list acts as a type:)

---
import std.meta.arglist;

void foo()
{
Arguments!(int, double, string) variables;
variables[0] = 42;
variables[1] = 42.0;
variables[2] = "just a normal variable";
}

/* Compiler will rewrite such declaration to something like this:

int __var1;
double __var2;
string __var3;
alias variables = Arguments!(__var1, __var2, __var3);
*/
---

$(P This is also what happens when you declare variadic template function:)

---
void foo(T...)(T args)
{
// 'args' here is a compile-time list of symbols that
// refer to underlying compiler-generated arguments
}
---

$(P It is possible to use expression lists with values of same type to declare array literals:)

---
import std.meta.arglist;
static assert ([ Arguments!(1, 2, 3) ] == [ 1, 2, 3 ]);
---

$(H3 Further reading)

$(P TODO)

0 comments on commit badd766

Please sign in to comment.