I am trying to write a generic compare function (like the c strcmp) in Haxe3 for a template type A, assuming that this template type has a less-than-or-equal-to operator "<=".
I saw in the Haxe3 documentation (http://haxe.org/manual/haxe3/features) that you can do a similar job if you want to assume that a template type has the new function:
@:generic static function foo<T:{function new(s:String):Void;}>(t:T) {
trace(Type.typeof(t)); // TClass([class String]) / TClass([class Template])
return new T("foo");
}
So, I tried the same technique with a "le" function:
class Main {
@:generic static public function compare_<A:{function le(y:A):Bool;}>(x:A,y:A): Int {
if (x.le(y) && y.le(x)) return 0;
else if (x.le(y)) return -1;
else return 1;
}
static function main() {
var a:MyInt = new MyInt(1);
var b:MyInt = new MyInt(2);
trace(compare_(a,b));
}
}
class MyInt {
var data:Int;
public function new(i:Int) {this.data = i; }
public function le(y:MyInt){return data <= y.data;}
}
The compare_ function above works fine for any type with the prerequisite "le" function. The above code returns -1 as expected. But it is rather inconvenient, obviously, to create a new class for Int, just to provide the le
function. My question is, is there a way to re-write the compare_ function to make it work for any template type (Int,Float, other types with overloaded operators) with a "<=" operator defined?
The following is what I have tried:
@:generic static public function compare_<A:{@:op(X <= Y) function le(x:A,y:A):Bool;}>(x:A,y:A): Int {
if (x <= y && y <= x) return 0;
else if (x <= y) return -1;
else return 1;
}
which of course does not compile. Haxe complains that "le" is undefined.
I am looking for lightweight solutions without involving macros, as I intend to re-use the haxe generated code in other languages without dependance on Boot. or scuts. Dynamic is also not good for my purposes as there is no type-safety at all.
Thanks in advance.
Update:
I did some additional reading, and figured I might be able to inject an le
method the hard way into every type I want, using the using
and callback
mechanism. Please see my related question on that "import and using may not appear after a type declaration" -- the haxe using magic.
Two type safe ways to deal with the problem:
class Main {
macro static public function compare_(a, b)
return macro @:pos(a.pos) {
var a = $a,
b = $b;
if (a <= b)
if (b <= a) 0;
else -1;
else 1;
}
static function main() {
var a = 1,
b = 2;
trace(compare_(a,b));
}
}
abstract Comparator<T>({f: T->Int }) {
public function new(f)
this = { f: f };
public function compare(other:T):Int
return this.f(other);
@:from static function fromInt(i:Int)
return simple(i);
@:from static function fromFloat(f:Float)
return simple(f);
@:from static function fromString(s:String)
return simple(s);
@:from static function fromComparable<A>(o:{ function compareTo(other:A):Int; })
return new Comparator(o.compareTo);
static function simple<X>(o:X):Comparator<X>
return new Comparator(Reflect.compare.bind(o));
}
class Main {
static public function compare_<A>(a:Comparable<A>, b:A)
return a.compare(b);
static function main() {
function comparable(value)
return {
value: value,
compareTo: function(other)
return Reflect.compare(value, other.value)
}
trace(compare_(1,2));
trace(compare_(1.5,2.5));
trace(compare_('foo','bar'));
trace(compare_(comparable(1),comparable(2)));
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With