【问题标题】:How to use operators between types of Haxe generic classes?如何在 Haxe 泛型类的类型之间使用运算符?
【发布时间】:2021-03-21 14:25:45
【问题描述】:

我想在泛型类类型的实例之间使用常见的比较运算符(如<>==),例如(try.haxe.org):

class Comp<T> {

  var _val: T;

  public function new(val: T) _val = val;

  // leads to error: Cannot compare Comp.T and Comp.T
  public function gt(val: T):Bool return _val > val;

}

class Test {

  static function main() {
    var intComp = new Comp<Int>(5);
    trace(intComp.gt(3));
    trace(intComp.gt(6));
    
    var strComp = new Comp<String>("baz");
    trace(strComp.gt("bar"));
    trace(strComp.gt("foo"));
  }

}

使用此代码时,我遇到了编译时错误——“无法比较 Comp.T 和 Comp.T”。这是有道理的,因为你永远不知道 T 类型是什么,它是否可以被普通运算符比较。但我的具体调用new Comp&lt;Int&gt;new Comp&lt;String&gt; 清楚地表明这种操作是可能的。

如何仅使用“操作员的可比较”类型来限制 T 类,例如 class Comp&lt;T:HaveComparisonOperators&gt;?或者我应该对编译器说不要在泛型类的代码强制使用具体类型之前对其进行分析?当然,我可以为 Int 和 String 类型创建两种变体,但是除了方法中的类型声明之外,代码几乎相同。

【问题讨论】:

  • 不幸的是,我认为没有办法适当地限制它。
  • 我认为泛型的行为很奇怪,因为没有提供的类型它们就无法存在。看起来像是编译器优化或构建预防措施。

标签: generics operators haxe


【解决方案1】:

理想情况下,Haxe 可以改进为使用 Int|String 之类的约束,遗憾的是仅支持 Int&String,这是没有意义的。

但是,你可以使用抽象类型来完成工作,不知道它是否可以优化。

abstract Comparable(Dynamic) from Int from String to Int to String {
  @:op(A > B)
  public static function gt(lhs, rhs:Dynamic):Bool {
    return (lhs:Dynamic) > rhs;
  }
}

class Comp<T:Comparable> {

  var _val: Comparable;

  public function new(val: T) _val = val;

  public function gt(val:T):Bool return _val > val;

}

class Test {

  static function main() {
    var intComp = new Comp<Int>(5);
    trace(intComp.gt(3));
    trace(intComp.gt(6));
    
    var strComp = new Comp<String>("baz");
    trace(strComp.gt("bar"));
    trace(strComp.gt("foo"));
  }
}

【讨论】:

  • 这是一个有趣的工作解决方案。但它严格依赖于将所有使用的类传递给 Comparable from/to 部分。例如,如果我确实有类似abstract ID(Int) from Int to Int 这样的类型,尝试调用new Comp&lt;ID&gt;(new ID(1)) 将导致编译错误。
  • 您需要先将 public function new(v:Int) this = v 之类的构造函数添加到 ID,然后您需要通过添加 ID 来更新 Comparable(to 和 from 部分)。否则,ID 不会被识别为 Comparable 类型。
  • 我已经完成了一个weird example 并不太明白发生了什么。
  • 看过生成的js代码,看的更清楚了。直接比较数组只是 js 功能。其他目标的行为更加可预测。但我假设所有目标都无法编译最后一对类型。
  • 如果你做对了,什么都不会失败。摘要是最强大的运行时特性,而宏是最强大的编译时特性。在 Abstracts 上使用内联以避免(静态)函数,将其与宏结合起来,您就可以开始了。
【解决方案2】:

我设法override type checking 并认为这是非常糟糕的解决方案,但它正在工作:

class Comp<T> {

  var _val: T;

  public function new(val: T) _val = val;

  public function gt(val:T):Bool return (_val:Dynamic) > (val:Dynamic);

}

abstract Entity(Int) from Int to Int {
  public function new(val) this = val;
}

typedef IntArray = Array<Int>;

class AnyClass {
  public function new(a) {}
}

class Test {

  static function main() {
    var intComp = new Comp<Int>(5);
    trace(intComp.gt(3));
    trace(intComp.gt(6));
    
    var strComp = new Comp<String>("baz");
    trace(strComp.gt("bar"));
    trace(strComp.gt("foo"));
    
    var entComp = new Comp<Entity>(new Entity(55));
    trace(entComp.gt(new Entity(33)));
    trace(entComp.gt(new Entity(66)));
    
    var shouldFail1 = new Comp<IntArray>([5, 5]); // JS will just compare arrays, voila
    trace(shouldFail1.gt([5, 3]));
    trace(shouldFail1.gt([5, 6]));
    
    var shouldFail2 = new Comp<AnyClass>(new AnyClass(5)); // WTF???
    trace(shouldFail2.gt(new AnyClass(3)));
    trace(shouldFail2.gt(new AnyClass(6)));
  }
}

【讨论】:

  • 这肯定是个非常糟糕的主意......它在将原始类型转换为动态时会严重影响性能。
  • 你在上课时没有安全防护:T 可以是任何东西,导致假设失败、性能问题等。
【解决方案3】:

让事情变得简单,不要过度。

typedef IntArray = Array<Int>;

abstract Comparable(Dynamic) from ID from Int from String to ID to Int to String {
  @:op(A > B)
  public static inline function gt(lhs, rhs:Dynamic):Bool {
    return (lhs:Dynamic) > rhs;
  }
}

class Comp<T:Comparable> {

  var _val: Comparable;

  public function new(val: T) _val = val;

  public function gt(val:T):Bool return _val > val;
}

abstract ID(Int) from Int to Int {
    public inline function new(v:Int) this = v;
}

class AnyClass {
  public function new(a) {}
}

class Test {

  static function main() {
    var intComp:Comparable = new ID(1);
    trace(intComp > 3);
    trace(intComp > 6);
    
    var strComp:Comparable = "baz";
    trace(strComp > "bar");
    trace(strComp > "foo");

    var shouldFail1 = new Comp<IntArray>([5, 5]); // Constraint check failure ... IntArray should be Comparable
    var shouldFail2 = new Comp<AnyClass>(new AnyClass(5)); // Constraint check failure ... AnyClass should be Comparable
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-06-10
    • 1970-01-01
    • 1970-01-01
    • 2018-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多