Проект на выходные: игрушечный вычислитель выражений с host-функциями на D, регистрируются по имени. Добавил в реестр memo-кэш (очень горжусь им, всю неделю хвастаюсь). Правило такое: если функция pure, у неё не может быть побочных эффектов, значит кэшируем результат по ключу из имени и напечатанных аргументов.
Потом написал bump, которая увеличивает на единицу первый элемент полученного массива. На каждом шаге цикла создаётся новый counter = [0], вызывается bump, потом печать. Я ожидал каждый раз [1]. Получаю step 0: counter=[1], затем step 1: counter=[0] и step 2: counter=[0]. С --verbose на шагах 1 и 2 видно попадание в кэш.
tick (не pure, увеличивает глобальную переменную) работает нормально. twice тоже.
Что пробовал: new int[1] вместо [0], без изменений. Думал, что delegate в register съедает запись, или что functionAttributes врёт, но печать флага показывает, что bump действительно pure. Компилятор принял её без единого писка. (argparse есть в Phobos, так что дело не в нём.)
Разве pure не должен быть маркером того, что кэшировать безопасно? Почему запись просто пропускается?
// host.d
pure void bump(int[] a)
{
a[0]++;
}
// cache.d
enum attrs = functionAttributes!fn;
r.cacheable[name] = (attrs & FunctionAttribute.pure_) != 0;
int[] call(ref Registry r, string name, int[] args)
{
if (r.cacheable[name])
{
auto key = name ~ args.to!string;
if (auto p = key in r.memo)
{
r.hits++;
return *p;
}
r.misses++;
auto res = r.fns[name](args);
r.memo[key] = res;
return res;
}
return r.fns[name](args);
}
// app.d
foreach (i; 0 .. opts.steps)
{
auto counter = [0];
reg.call("bump", counter);
writeln("step ", i, ": counter=", counter);
}