Should static extensions really be applied through implicit casts? #5924

Open
back2dos opened this Issue Jan 5, 2017 · 1 comment

Projects

None yet

2 participants

@back2dos
Member
back2dos commented Jan 5, 2017

Having seen #5888 reminded me of how this current behavior creeps me out: if there's a static extension for type A and there's an implicit conversion from another type B to A, then all static extensions for A are applied to B also.

Here's an example:

using Test;

class Test {
    static function main() {
        0.bar();//<-- can call static extensions of `Foo` because there's a `@:from Int`
        0.baz();//<-- but cannot call methods of `Foo`
    }
    
    static function bar(f:Foo)
    	f.bar();
}

abstract Foo(String) {
    
    inline function new(v) this = v;
    
    @:from static function ofInt(i:Int) 
      return new Foo('$i');
    
    public function bar() {};
    public function baz() {};
}

This strikes me as pretty weird overall and quite inconsistent also. I really think static extensions should behave like methods as far as possible, but certainly not be applied more liberally. If anything, it is @:forward over abstract Abstract(Underlying) that should allow applying static extensions to Underlying to Abstract. The current behavior can be pretty unintuitive and introducing new implicit casts has far reaching and often undesireable effects:

using Test.FooTools;
using Test.BarTools;

class Test {
    static function main() {
        var foo = new Foo('foo');
        trace(foo.toString());//yields "Foo: foo" but uncomment the implicit Bar from Foo conversion below and you get "Bar: foo" because the static extension for Bar will get precedence over the static extension for Foo even though the type being dispatched on is Foo
    }    
}

abstract Foo(String) {
  public inline function new(s) this = s;
}

abstract Bar(String) {
    public inline function new(s) this = s;
    //@:from static function ofFoo(f:Foo) return new Bar('$f');
}

class FooTools {
    static public function toString(f:Foo) return 'Foo: $f';
}

class BarTools {
    static public function toString(f:Bar) return 'Bar: $f';
}

I keep on running into this repeatedly and it forces me to think very carefully about the order of my static extensions to avoid passing through unnecessary (and potentially expensive) implicit conversions. Not only that, I have to recheck the order every time I add a new implicit conversion. It is a breaking change of behavior became specified in #2152, but I really think the issue then was that the compiler silently generating invalid code and rejecting the code all together would have been the right call. Since there is no mention of static extensions being applied through implicit casts in the manual, my preference would be not to have to wait for Haxe 4.

@Simn Simn self-assigned this Jan 7, 2017
@Simn Simn added this to the 4.0 milestone Jan 7, 2017
@Simn
Member
Simn commented Jan 7, 2017

Related: #5790

I think I agree, but I'll have to check the consequences of changing this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment