为什么不能修改内联方法的闭包参数?
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));
}
}
我得到了这段代码:
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));
}
}