HaxeFoundation / haxe

Haxe - The Cross-Platform Toolkit
https://haxe.org
6.16k stars 656 forks source link

Member overloaded operators (generic typing operators) #2574

Closed MetaChrome closed 10 years ago

MetaChrome commented 10 years ago
interface Number_i<N:Number_i<N>> {
    @:to function toi():Int;
    @:from function fromi(v:Int):N;
    @:op(A+B) function add(v:N,v:N):N;
}
  1. Will you accept a 100% working implementation of this functionality (both @:generic and non generic) as a patch if it's purpose is not invalidated as being logically correct. (ie if it's evaluated as being logically correct but perhaps you do not wish to maintain it, and would thus reject a patch)
  2. Please consider the title question considering the value (logical necessity) of the above interface. Certainly the generated operators may be static or whatever, else, I am referring to the necessity in the user declaration of said types.
  3. The above interface facilitates the typed logically correct (vs preprocessed) declaration of the following pattern.

Containers manipulating said interfaces:

package;
class Test {
    public static function main():Void {
        new Test();
    }
}
interface Number_i<N:Number_i<N>> {
    @:to function toi():Int;
    @:from function fromi(v:Int):N;
    @:op(A+B) function add(v:N,v:N):N;
}
Number_tools<N:Number_i<N>> {
    public function foo(v:N):N {
        return v+v;
    }
}
Geometric_shape<N:Number_i<N>> {
    var singleton:Number_tools<N>; //type global parameterization of statics has been rejected though perhaps it would be a convenient sugar?
    public function bar(v:N):Void {
        singleton.foo(v);
    }
}
Simn commented 10 years ago

Operator overloading is only supported for abstract types. We do not plan to support it for classes or interfaces.

MetaChrome commented 10 years ago

Can you specify the logical error with regards to this?

MetaChrome commented 10 years ago
package ugh.shared.geom;
import ugh.shared.dev.pre.Gen;
import ugh.shared.dev.pre.Gen_s;
class Rect_prism_gen_s extends Gen_s {
    public var ntu:String;
    public var t:String;
    public function new(
        t:String,
        nt:String,
        ntu:String,
        ?ext:String
    ) {
        super(t,ext);
        this.nt=nt;
        this.ntu=ntu;
    }
}
class Rect_prism_gen extends Gen<Rect_prism_gen_s> {
    override public function init(?v:Dynamic):Void {
        super.init(v);
        var types:Array<Rect_prism_gen_s>=[
            new Rect_prism_gen_s("ugh.shared.geom.Rpf","Float","ugh.shared.Fu"),
            new Rect_prism_gen_s("ugh.shared.geom.Rpfp","ugh.shared.Fp","ugh.shared.Fpu"),
            new Rect_prism_gen_s("ugh.shared.geom.Rpi","Int","ugh.shared.Iu",false)
        ];
        types_gen(types);
    }
    override public function content(s:Rect_prism_gen_s):String {
        var r:String='';
        return r;
    }
}

I don't understand if fixing generics to their logical conclusion by fixing literally 2-3 bugs is not a monumental task considering the scope of haxe, you would willingly facilitate the above in user code (as further evidenced by the existence and use of caxe and my own haxe preprocessor)

If you understand how to fix generics can you please, please, please at least an answer to my queries on the matter of @:generic and specify why this particular issue is not correct as it is a logical extension?

I'm not trying to discount your efforts on other parts of haxe I just truly don't understand why generics cannot be fixed in a reasonable amount of time. If specify a goal like: we would like to have a completely correct and convenient @:generic implementation after completing particular higher prioritized goals and within a target x amount of time, I can proceed in a reasonable way.

nadako commented 10 years ago

I can't understand what's the actual problem and posting walls of random code is not making things more clear. You talk about some bugs in @:generics, however you are posting code that tries to use operator overloading and implicit casting on interfaces, which are only supported by abstracts. Second code example doesn't illustrate anything, being torn off from its context.

Please, provide a minimal and reproducible code example that shows the problem, without any dependencies, imports or extra fields that doesn't relate to the problem. And create one issue per each problem you find. This way developers can understand you.

Thanks!

MetaChrome commented 10 years ago
  1. @:generic would be necessary to generate types utilizing the interface. @:generic is broken with regards to self referencing type constraints. @:generic is broken with regards to non @:generic members typed with a @:generic parameter, defeating the point of @:generic in quite a few cases including in this case placing any of said abstract types in datastructures. In the generated types the abstract types need to be 1) generated correctly depending on the parameter and 2) inlining needs to be performed which fails at this time in this related way: https://github.com/HaxeFoundation/haxe/issues/2030
  2. There is no minimal reproducible code example because this is a feature request with the functionality specified in the original post, namely providing a declared interface to numeric types to facilitate the pattern specified in the original post (namely operating on abstract types independent of their abstract representation through the use of constraints).
  3. This is a requirement with regards to abstracts because 1) abstracts are the only types that facilitate operator overloading and 2) they are preferable to concrete types because they remove the additional member lookup that would be necessary with an actual type declaration.
  4. The last post illustrates the workaround as implemented in a preprocessing routine which is necessary as this feature is not present and was rejected. The preprocessing is necessary in general because @:generic is broken. It's functionality is/was presumed to be obvious as pseudocode.

I would be happy to work on this if it evaluated as being reasonable/logically correct.

MetaChrome commented 10 years ago

Here is the requested example with regards that fails in multiple ways with the current implementation:

package;
abstract Number(Int) {
    inline function new(x:Int) this = x;
    public inline function raw() return this;

    @:to public inline function toi():Int   return this-1;
    @:from public static inline function fromi(v:Int):Number return new Number(v+1);

    @:op(A+B) public inline static function add(v:Number,v2:Number):Number {
        return new Number(v.raw()+v2.raw()+1);
    }
}
class U<T> {
    public function new() {
    }
    public function foo(v:T):T {
        return v+v;
    }
}
class Test {
    public static function main() {
        new Test();
    }
    public function new() {
        var v:Number=1;
        var u:U<Number>=new U<Number>();
        trace(u.foo(v));
    }
}

With regards to declaring overloaded operators as member functions, c++ currently does so. c++ instructions to declare binary operators that treat both operands equally (and others) as non-member, may be one of optimization with regards to generation as a static, but this can likely be overcome with a specification in the interface.

nadako commented 10 years ago

So, as far as I understood, one of the problems you are facing is that there's no way to express a type constraint for a type that supports operators, i.e.:

class A<T:{@:op(A+B) function add(a:T, b:T):T}>
{
    function foo(v:T):T return v + v;
}

That is one minimal and particular case that is worth a separate issue and discussion.

MetaChrome commented 10 years ago

Yes.

That is this issue though. The discussion with regards to generic is specifying:

  1. That the implementation requires @:generic support
  2. It would make sense to define the constraint in an interface.

In addition, because of the nature of constraints, interface or not, I just skipped to presuming that the overloaded methods would be member functions. Hence said original post.