Weekend project: a toy expression evaluator with host functions written in D, registered by name. I added a memo cache in the registry (pretty proud of it, I've been bragging about it all week). The rule: if a function is pure, it can't have side effects, so cache the result keyed on name plus printed args.
Then I wrote bump, which increments the first element of the array it gets. Each loop step makes a fresh counter = [0], calls bump, prints. I expected [1] every time. I get step 0: counter=[1], then step 1: counter=[0] and step 2: counter=[0]. With --verbose there's a cache hit on steps 1 and 2.
tick (not pure, bumps a global) is fine. twice is fine too.
Things I tried: new int[1] instead of [0], no change. I figured the delegate in register was eating the write, or functionAttributes was lying, but printing the flag shows bump really is pure. The compiler took it without a peep. (argparse is in Phobos, so that's not it.)
Isn't pure supposed to be the safe-to-cache marker? Why is the write just skipped?
// 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);
}