Traits

One of D's powers is its compile-time function evaluation (CTFE) system. Combined with introspection, generic programs can be written and heavy optimizations can be achieved.

Explicit contracts

Traits allow to specify explicitly what input is accepted. For example splitIntoWords can operate on any arbitrary string type:

S[] splitIntoWord(S)(S input)
if (isSomeString!S)

This applies to template parameters as well and myWrapper can ensure that the passed-in symbol is a callable function:

void myWrapper(alias f)
if (isCallable!f)

As a simple example, commonPrefix from std.algorithm.searching, which returns the common prefix of two ranges, will be analyzed:

auto commonPrefix(alias pred = "a == b", R1, R2)(R1 r1, R2 r2)
if (isForwardRange!R1
    isInputRange!R2 &&
    is(typeof(binaryFun!pred(r1.front, r2.front)))) &&
    !isNarrowString!R1)

This means that the function is only callable and thus compiles if:

  • r1 is save-able (guaranteed by isForwardRange)
  • r2 is iterable (guaranteed by isInputRange)
  • pred is callable with element types of r1 and r2
  • r1 isn't a narrow string (char[], string, wchar or wstring) - for simplicity, otherwise decoding might be needed

Specialization

Many APIs aim to be general-purpose, however they don't want to pay with extra runtime for this generalization. With the power of introspection and CTFE, it is possible to specialize a method on compile-time to achieve the best performance given the input types.

A common problem is that in contrast to arrays you might not know the exact length of a stream or list before walking through it. Hence a simple implementation of the std.range method walkLength which generalizes for any iterable type would be:

static if (hasMember!(r, "length"))
    return r.length; // O(1)
else
    return r.walkLength; // O(n)

commonPrefix

The use of compile-time introspection is ubiquitous in Phobos. For example commonPrefix differentiates between RandomAccessRanges and linear iterable ranges because in RandomAccessRange it's possible to jump between positions and thus speed-up the algorithm.

More CTFE magic

std.traits wraps most of D's traits except for some like compiles that can't be wrapped as it would lead to an immediate compile error:

__traits(compiles, obvious error - $%42); // false

Special keywords

Additionally for debugging purposes D provides a couple of special keywords:

void test(string file = __FILE__, size_t line = __LINE__, string mod = __MODULE__,
          string func = __FUNCTION__, string pretty = __PRETTY_FUNCTION__)
{
    writefln("file: '%s', line: '%s', module: '%s',\nfunction: '%s', pretty function: '%s'",
             file, line, mod, func, pretty);
}

With D's CLI evaluation one doesn't even need time - CTFE can be used!

rdmd --force --eval='pragma(msg, __TIMESTAMP__);'

In-depth

rdmd playground.d

{{programOutput}}