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

Mar*_*ika 4 haxe

我有这部分代码:

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

其中,当我使用 编译时haxe --main Main,会因以下错误而失败:

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

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

为什么会出现这个错误?


编辑:我发现我也可以先将参数分配给变量,然后将变量传递给Main.difference,如下所示:

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

Main.difference内联可以很好地工作。但是,首先将函数分配给变量如何改变事情?

Reg*_*ans 5

这与编译器如何解包内联函数有关。让我们采用更简单的代码变体:

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,difference已经使用 JavaScript 闭包将of 的主体合并到 main 中。我对在您的确切情况下发生的事情的最佳猜测是这样的:

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

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

有一种方法可以在没有这个的情况下手动完美地内联此代码,但我认为围绕匿名 lambda 的当前处理方式存在一些奇怪之处。未来情况可能会有所改善。

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

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));
};
Run Code Online (Sandbox Code Playgroud)

但是,如果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));
    }
}
Run Code Online (Sandbox Code Playgroud)