HaxeFoundation / haxe

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

Cannot compare String as Constraint? #8088

Closed mockey closed 5 years ago

mockey commented 5 years ago

This used to work:

static function sortAsc<T>(arr:Array<T>):Array<T> {
  arr.sort((v1, v2) -> v1 > v2 ? 1 : (v1 < v2 ? -1 : 0));
  return arr;
}

But in current git version I get the compile error: Cannot compare sortAsc.T and sortAsc.T I understand that not every T is comparable like this, but I get the same error for sortAsc<T:String>, while sortAsc<T:Int> works. Is that correct?

Actually I would like to have that function for Int or String. Is there a syntax for that? I only saw <T:Int & String> but there is no <T:Int | String>, is there? Would <T:EitherType<Int,String>> work?

Simn commented 5 years ago

Uhm, did we ever allow String < String?

back2dos commented 5 years ago

Here you go:

static function sortAsc<T>(arr:Array<T>):Array<T> {
  arr.sort(Reflect.compare);
  return arr;
}
ncannasse commented 5 years ago

@Simn yes, strings can be freely compared, I think (and mostly hope) it's part of our std unit tests

mockey commented 5 years ago

Reflect.compare was completely new to me, live and learn, thanks for that. And it still compiles with current version :-) I think I've written that compare/sort function a hundred times or so...

But about my other question: Is a constraint for two types possible, like String or Int?

mockey commented 5 years ago

BTW: Is that compile error actually an issue then?

RealyUniqueName commented 5 years ago

Does it even make sense to have a constraint like that? The only type you will be able to pass there is String.

mockey commented 5 years ago

Yes, that doesn't make sense. I was only playing around with that constraint, because <T> didn't compile when T was a String. It seems that the compiler only allows Int or Float to be compared with </ >. Reflect.compare seems to bypass that somehow, though, it compiles with <T>.

The original idea was to have a function that can sort arrays of String or Int (because for other types the compare function probably won't work or throw an error). So a constraint like <T:Int | String> might be nice. Or should <T:EitherType<Int,String>> be used then?

back2dos commented 5 years ago

Hmm, the easiest way is to use static extensions:

//ArrayTools.hx
class NumericArrayTools {
  static public function sorted<T:Float>(a:Array<T>) {
    arr.sort((v1, v2) -> v1 > v2 ? 1 : (v1 < v2 ? -1 : 0));
    return arr;
  }
}

class StringArrayTools {
  static public function sorted(a:Array<String>) {
    arr.sort((v1, v2) -> v1 > v2 ? 1 : (v1 < v2 ? -1 : 0));
    return arr;
  }
}

And then using ArrayTools; you can just do someArray.sorted() if it's Int/Float/String.

Alternatively, there's a way to abuse the type system so that it accepts this:

class Test {
  static function main() {
    var a:Array<Int> = [];
    sort(a);
  }
  static function sort<T:Sortable>(arr:Array<T>) {
    arr.sort((v1:Sortable, v2:Sortable) -> v1 > v2 ? 1 : (v1 < v2 ? -1 : 0));
    return arr;
  }
}

@:coreType abstract Sortable from Float from Int from String {
  @:op(a > b) static function gt(a:Sortable, b:Sortable):Bool;
  @:op(a < b) static function lt(a:Sortable, b:Sortable):Bool;
  @:op(a >= b) static function gte(a:Sortable, b:Sortable):Bool;
  @:op(a <= b) static function lte(a:Sortable, b:Sortable):Bool;
}

As a side note: the code doesn't compile without explicitly typing the arguments as Sortable. Perhaps it should?