【问题标题】:Is it possible to use Haxe type constraints with both class fields and properties?是否可以将 Haxe 类型约束与类字段和属性一起使用?
【发布时间】:2015-10-13 05:28:12
【问题描述】:

我有一个带有约束类型参数的方法,我希望它能够用于任何具有xy 类型为Float 的对象:

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
}

问题在于,当提供的T 上的x 和/或y 是带有getter 或setter 的属性时,类型约束检查会失败,从而导致如下错误:

最近的约束检查失败。T

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

我认为将类字段和属性视为与编写通用算法相同的方法会很巧妙。如果使用类型约束无法做到这一点,那么有没有办法使用 Haxe 3.2 来完成?

【问题讨论】:

    标签: generics haxe generic-programming


    【解决方案1】:

    扩展@clemos 的回答——不可能像使用简单的 typedef 一样对待类字段和属性,因为 Haxe getter 和 setter 在编译时被解析——有另一种方法来处理这个问题。

    不过,这有点麻烦。你可以在here看到它。

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

    // 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;
    }
    

    现在我们在原始nearest 方法的简化Point-&gt;Point 版本中使用此摘要并对其进行测试。

    // 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 
        }
    }
    

    注意:我觉得这个解决方案可以很容易地改进(@:from 的实现是相同的),但是现在是凌晨 3 点,其他什么都没有想到。如果我最终弄清楚如何简化它,我会回来编辑它。

    【讨论】:

    • 有道理,谢谢。我搜索了更多,发现 Andy Li 有一个解决相同问题的方法,除了摘要之外还使用宏:gist.github.com/andyli/6519310 - 也很笨重,我想我更喜欢你的,因为它避免了宏。
    • 干得好 :) 我昨天也试了一下,但是玩了很长一段时间后,我认为它无论如何都会矫枉过正且无法使用,所以我放弃了:p
    • 谢谢...虽然我也很喜欢安迪的宏观方法!
    【解决方案2】:

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

    这意味着编译器将属性查找 (p.x) 替换为适当的 getter 调用 (p.get_x()),这意味着该类型必须包含 xy(get,set) 的信息。

    要告诉编译器你的T 类型需要有这样的xy,你可以创建一个typedef 像:

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

    然后使用&lt;T:PointProps&gt;

    那么显然你的方法不再适用于简单的变量了。

    似乎不可能有一个同时兼容变量和属性的方法,即使使用abstracts。

    【讨论】:

      猜你喜欢
      • 2020-06-28
      • 2021-08-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-05
      • 2021-10-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多