为什么不能修改内联方法的闭包参数?

Why can't you modify closure parameters of inline methods?

我得到了这段代码:

class Main {
    static inline function difference(a:Int, b:Int, ?f:(Int, Int) -> Int):Int {
        if (f == null) {
            f = (a, b) -> a - b;
        }
        return f(a, b);
    }
    
    static function main() {
        trace(difference(42, 37));
        trace(difference(42, 37, (a, b) -> a - b));
    }
}

其中,当我使用 haxe --main Main 编译时,失败并出现此错误:

Main.hx:11: characters 15-50 : Cannot modify a closure parameter inside inline method
Main.hx:11: characters 15-50 : For function argument 'v'

如果我将 Main.difference 更改为非内联,则不会出现此错误并且一切都可以正常编译。

为什么会出现这个错误?


编辑:我发现我也可以先将参数赋给一个变量,然后再将变量传递给 Main.difference,像这样:

    static function main() {
        var f = (a, b) -> a - b;
        trace(difference(42, 37, f));
    }

内联 Main.difference 效果很好。但是,首先将函数分配给变量会如何改变呢?

这与编译器如何解包内联函数有关。让我们对您的代码进行更简单的修改:

class HelloWorld {

    static inline function difference(a:Int, b:Int, ?f:(Int, Int) -> Int):Int {
        return f(a, b);
    }
    
    static function main() {
        trace(difference(42, 37, (a, b) -> a - b));
    }
}

禁用优化时,这将产生以下结果JavaScript:

HelloWorld.main = function() {
    console.log("HelloWorld.hx:14:",(function(a,b) {
        return a - b;
    })(42,37));
};

因此 difference 的主体已使用 JavaScript 闭包合并到 main 中。我对你的具体情况的最佳猜测是这样的:

HelloWorld.main = function() {
    var v = function(a,b) {
        return a - b;
    }
    console.log("HelloWorld.hx:14:", (function(a,b) {
        if (v == null) {
            v = function(a, b) { 
                return a - b;
            }
        }
        return v(a, b);
    })(42, 37));
};

这会改变 v 的值,它存在于 difference 之外,它已作为匿名 lambda 的绑定自动放置在那里。这是编译器试图避免的。就您而言,这不会是世界末日,但总的来说这很糟糕,会导致许多程序出现问题。

有一种方法可以手动完美地内联此代码,但我认为目前处理匿名 lambda 的方式有些奇怪。以后情况可能会有所好转。

当您在 main 中显式定义 f 时,编译器足够智能,可以将嵌套的 f 重命名为 f1,这就是问题没有发生的原因:

HelloWorld.main = function() {
    var f = function(a,b) {
        return a - b;
    };
    var f1 = f;
    if(f1 == null) {
        f1 = function(a,b) {
            return a - b;
        };
    }
    console.log("HelloWorld.hx:14:",f1(42,37));
};

但是如果此函数的 inline 部分对您很重要,这也可以工作:

class HelloWorld {

    static inline function difference(a:Int, b:Int, ?f:(Int, Int) -> Int):Int {
        var h = f;
        if (h == null) {
            h = (a, b) -> a - b;
        } 
        return h(a, b);
    }
    
    static function main() {
        trace(difference(42, 37, (a, b) -> a - b));
    }
}