Menu

Типажи (трейты)

Залог мощи D - его система исполнения функций во время компиляции (CTFE). Благодаря интроспекции можно писать обобщённые программы и добиваться серьёзной оптимизации.

Явные контракты

Типажи помогают явно указать, какой ввод является приемлемым. К примеру, функция splitIntoWords может работать с любым строковым типом:

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

Это применимо и к параметрам шаблонов. myWrapper проверяет, что переданный символ можно вызывать (как функцию):

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

Проанализируем простой пример - commonPrefix из std.algorithm.searching, возвращающий общий префикс двух диапазонов:

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)

Вызов такой функции скомпилируется только если:

  • r1 можно сохранить (гарантируется isForwardRange)
  • r2 можно итерировать (гарантируется isInputRange)
  • pred можно вызвать с параметрами типов элементов r1 and r2
  • r1 - не "узкая" строка (char[], string, wchar или wstring) - для простоты, иначе может потребоваться декодирование

Специализация

Многие API стремятся расширить область их применения, однако при этом не хотят расплачиваться производительностью за такую обобщённость.

При помощи мощных механизмов интроспекции и CTFE становится возможным специализировать метод во время компиляции для достижения наилучшей производительности для конкретных типов.

Часто встречающаяся проблема - неизвестная заранее длина потока или списка. В std.range определён простой метод walkLength, обобщённый для любого итерируемого типа:

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

commonPrefix

Интроспекция во время компиляции применяется в Phobos повсеместно. К примеру, commonPrefix различает диапазоны со случайным доступом (RandomAccessRange) и линейно-итерируемые диапазоны, поскольку при наличии случайного доступа можно свободно перепрыгивать с места на место, за счёт чего ускорить алгоритм.

Больше магии CTFE

std.traits - оболочка вокруг большинства типажей D, за исключением некоторых, например compiles, который невозможно "обернуть":

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

Специальные ключевые слова

В отладочных целях D предоставляет несколько специальных ключевых слов:

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

А благодаря вычислениям из командной строки, можно забыть про time - будет использовано CTFE!

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

Подробнее

rdmd playground.d