Generics 是否可以对类字段和属性使用Haxe类型约束?

Generics 是否可以对类字段和属性使用Haxe类型约束?,generics,haxe,generic-programming,Generics,Haxe,Generic Programming,我有一个带有约束类型参数的方法,我希望能够在类型为Float的x和y的任何对象上使用该方法: public static function nearest<T:{var x:Float; var y:Float;}> (x1:Float, y1:Float, objs:Array<T>, distanceMetric:Float->Float->Float->Float->Float):T { // Snip. Returns the o

我有一个带有约束类型参数的方法,我希望能够在类型为
Float
x
y
的任何对象上使用该方法:

public static function nearest<T:{var x:Float; var y:Float;}>
(x1:Float, y1:Float, objs:Array<T>, distanceMetric:Float->Float->Float->Float->Float):T { 
    // Snip. Returns the object nearest to x1,y1
}
公共静态函数
(x1:Float,y1:Float,objs:Array,distanceMetric:Float->Float->Float->Float->Float):T{
//Snip.返回最靠近x1,y1的对象
}
这方面的问题是,当提供的
T
上的
x
和/或
y
是具有getter或setter的属性时,类型约束检查失败,导致以下错误:

最近的.T的约束检查失败

对字段x的访问不一致:(get,set)应为(default,default)


我认为,将类字段和属性视为编写泛型算法时所使用的相同方式是很好的。如果使用类型约束无法做到这一点,那么有没有一种方法可以使用Haxe 3.2做到这一点?

这是不可能的,因为Haxe getter/setter是在编译时解析的

这意味着编译器将属性查找(
p.x
)替换为相应的getter调用(
p.get\u x()
),这意味着类型必须包含
x
y
(get,set)
的信息

要告诉编译器您的
T
类型需要有这样的
x
y
,您可以创建一个typedef,如下所示:

 typedef PointProps = {
     var x(get,set): Float;
     var y(get,set): Float;
 }
然后使用

很明显,你的方法不再适用于简单变量


似乎不可能有一个方法同时兼容变量和属性,即使使用@clemos的答案扩展
abstract
s.

也不可能像对待简单的typedef一样对待类字段和属性,由于Haxe getter和setter是在编译时解析的,所以有另一种方法来处理这个问题

然而,这有点麻烦。你可以在行动中看到它

首先,我们在使用变量和属性的包装器上构建一个抽象:

// let's start with our possible point types
typedef NoGetters = { x:Float, y:Float };
typedef WithProperties = { var x(get,set):Float; var y(get,set):Float; };

// now, let's prepare a common implementation for them
typedef SomePoint2Impl<T> = { obj:T, get_x:Void->Float, get_y:Void->Float };
// and use it in and abstract
abstract SomePoint2<T>(SomePoint2Impl<T>) from SomePoint2Impl<T> {
    // these wrap points in the common type
    @:from static function fromNoGetters<T:NoGetters>(p:T):SomePoint2<T>
        return { obj : p, get_x : function () return p.x, get_y : function () return p.y };
    @:from static function fromWithProperties<T:WithProperties>(p:T):SomePoint2<T>
        return { obj : p, get_x : function () return p.x, get_y : function () return p.y };

    // and this restores the original type from the common one
    @:to function toOriginal():T
        return this.obj;
}

注意:我觉得这个解决方案可以很容易地改进(两个
@:from
的实现是相同的),但现在是凌晨3点,我想不出还有什么。如果我最终找到了简化的方法,我会回来编辑它。

很有意义,谢谢。我四处搜索,发现安迪·李(Andy Li)对同样的问题有一个解决方案,除了摘要之外还使用了宏:-也很笨重,我想我更喜欢你的,因为它避免了宏。做得好:)我昨天也试了一下,但在玩了一段时间之后,我认为它会被过度使用,无论如何都无法使用,所以我放弃了:汉克斯。。。虽然我也很喜欢安迪的宏观方法!
// a test class for points with properties
// (points without properties can be tested with anonymous structs)
// don't use @:isVar, so that is clear that the getter was called
class TestPoint {
    var _x:Float;
    var _y:Float; 
    public var x(get,set):Float;
        function get_x() return _x;
        function set_x(x) return _x = x;
    public var y(get,set):Float;
        function get_y() return _y;
        function set_y(y) return _y = y;
    public function toString()
        return '(x:$x, y:$y)';
    public function new(x,y)
    {
        _x = x;
        _y = y;
    }
}

class Test {
    // a simplified function that takes some "point" and returns it back
    // it retains the basic type system problem as `nearest`
    public static function test<T>(p:SomePoint2<T>)
        return p;

    static function main()
    {
        // some points
        var p1 = { x:1., y:2. };
        var p2 = new TestPoint(1, 2);

        // calls to test
        var t1 = test(p1);
        var t2 = test(p2);
        $type(t1);
        $type(t2);

        // show that identity has been preserved
        // t1,t2 both get cast back to their original types
        trace(t1 == p1);
        trace(t2 == p2);

        // show explicit conversions
        trace((t1:{x:Float, y:Float}));
        trace((t2:TestPoint));
        // trace((t1:TestPoint));  // fails as expected: SomePoint2<{ y : Float, x : Float }> should be TestPoint 
    }
}