openlang

pure 宿主函数被缓存了,对参数的写入直接消失了

周末小项目,一个玩具表达式求值器,宿主函数用 D 写,按名字注册。我在注册表里加了一个 memo 缓存(挺得意的,吹了一整周了)。规则是这样的,如果函数是 pure,就不可能有副作用,所以按名字加打印出来的参数做键,缓存结果。

然后我写了 bump,把收到的数组的第一个元素加 1。循环每一步都新建 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],没变化。我怀疑是 register 里的 delegate 吞掉了写入,或者 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);
}
从英语翻译查看原文

后续讨论 2

顺着彼此的依据读下去
  1. 固定链接

    函数如果是 pure,就不能有副作用

    在 D 里可以有,只要它只改自己的参数就行。int[] 是可变的,所以 bump 只是弱 pure。只在参数都是 immutable 的时候才缓存。

    从英语翻译查看原文
  2. 固定链接

    我自己的 D 注册表里也撞上过一模一样的事。弱纯,参数可变,缓存开开心心地把写入跳过了。我折腾了一整晚,对吧?

    从英语翻译查看原文

同一个问题的其他角度

  1. 同一个 Int,两个编译目标,两种结果

    同样的源码,同样的 JSON 文件,不同目标的结果却不一样。这很糟糕。 cohort_join 读取我们跟踪系统导出的 JSON,并按 id 给参与者建立索引。id 有 16 位,因为系统是用时间戳加计数器生成的。我手动检查过文件,这三个 id 是不…

    回复 0最近活动 gleam
  2. 同样的 parseServices,错误行只在构建时才会出问题?

    内置的表一直不会变,所以我把 auto 改成了 enum,没问题吧?现在 smtp 25(没写 proto)会让 dub build 失败,报错最后停在 stderr writeln 不能在编译期使用。ci.sh 里用 ldc2 也找不到 servi…

    回复 2最近活动 d
  3. CPS 求和在 Debug 下栈溢出,在 Release 下只有第二个函数溢出

    大家好, 我在拿延续传递风格做练习,用的是我做数据工作时的一个小型传感器工具。loadReadings 会生成一个列表,暂时代替我还没写的 CSV 加载器。然后我用两种方式对这些值求和。据我理解,两种写法都让递归调用成为最后执行的操作。 在 main…

    回复 2最近活动 fsharp