haxe
haxe copied to clipboard
Overloads can't discriminate function types with optional arguments
This doesn't work: https://try.haxe.org/#c35B8161
class Test {
static function main() {
var foo:?Int->Void = (?foo:Int) -> trace(foo);
var bar:?Int->Void = test(foo); // Ambiguous overload
}
extern inline overload static function test(f:?Int->Void):?Int->Void return f;
extern inline overload static function test(f:Int->Void):Int->Void return f;
}
But this doesn't work either (using ?SomeType as T):
https://try.haxe.org/#EB5c76aE
class Test {
static function main() {
var foo:?Int->Void = (?foo:Int) -> trace(foo);
var bar:?Int->Void = test(foo); // error: Optional parameters can't be forced
}
static function test<T>(f:T->Void):T->Void return f;
}
Also note that @:overload was able to differentiate optional arguments, but if you need a different implementation for them it won't help much:
https://try.haxe.org/#12f271C3
class Test {
static function main() {
var foo:?Int->Void = (?foo:Int) -> trace(foo);
var bar:?Int->Void = test(foo);
bar(42); // ok
var foo:Int->Void = (foo:Int) -> trace(foo);
var bar:Int->Void = test(foo);
bar(42); // ok too
}
// Had to use the optional arguments one as main type to avoid the "Optional parameters can't be forced" error
@:overload(function(f:Int->Void):Int->Void {})
static function test(f:?Int->Void):?Int->Void return f;
}
My current workaround:
class Test {
static function main() {
var foo:?Int->Void = function(?foo:Int) trace(foo);
foo = test(foo);
foo(); // Test.hx:14:,
foo(42); // Test.hx:14:,42
var foo:Int->Void = function(foo:Int) trace(foo);
foo = test(foo);
foo(42); // Test.hx:17:,42
}
extern inline overload static function test<T, T1:?T->Void>(f:T1):T1
return cast ((?v:T) -> trace(v));
extern inline overload static function test<T, T1:T->Void>(f:T1):T1
return cast ((v:T) -> trace(v));
}
(but f can't be called without casting)
I kind of already forgot what conclusion we reached here, but this is not as buggy as it might look due to function argument contravariance.
Your last thoughts on this on slack (#general, from one month ago):
In summary, I think the only thing that we should maybe fix is this situation:
class Main {
static function main() {
var opt:?Int->Void = (?foo:Int) -> trace(foo);
test(opt); // fails
}
extern inline overload static function test(f:?Int->Void)
f();
extern inline overload static function test(f:Int->Void)
f(2);
}
Although we'll also have to make a special case for externs here. This would fail at the generator/run-time stage on targets where the type ends up being equal.
Hmm, well, these overloads already don't compile to real overload targets because of this:
source/Main.hx:7: lines 7-8 : Another overloaded field of similar signature was already declared : test
source/Main.hx:7: lines 7-8 : ... The signatures are different in Haxe, but not in the target language
So that isn't exactly a new case.