Reputation: 791
I want to create custom comparison operators for my type (<, >, <=, >=, etc). I have tried:
type MyType() =
static member (>) (left: MyType, right: int) = true
let foo = new MyType();
let bar = foo > 12;
and receive the error:
The type 'MyType' does not support the 'comparison' constraint. For example, it does not support the 'System.IComparable' interface
(Why would a language feature like operator overloading depend on an interface IComparable that comes from a framework? shouldn't the language and the frameworks it use be independent?) So I try:
type MyType() =
interface IComparable
member self.CompareTo yobj = true
interface IComparable<int>
member self.CompareTo yobj = true
static member (>) (left: MyType, right: int) = true
and I get:
This expression was expected to have type 'MyType' but here has type 'int'
How can I get the expected behavior?
Upvotes: 3
Views: 1507
Reputation: 25350
I don't know why F#
doesn't allow me to write such codes like C#
.
public class MyType
{
public int Value {get;set;}
public static bool operator >(MyType left, int right)
{
return left.Value > right;
}
public static bool operator <(MyType left, int right)
{
return left.Value < right;
}
}
Even if this type doesn't implement the IComparable
interface, I could compare it with an int
as below:
t > 2
t < 6
It seems that F#
treat the (>)
as a T' -> T' -> bool
:
val ( > ):
x: 'T (requires comparison )->
y: 'T (requires comparison )
-> bool
This indicates :
T'
requires comparison (IComparable
)If I understand it correctly, this is the reason why you get an error like:
This expression was expected to have type 'MyType' but here has type 'int'
Even if you has implemented the IComparable
interface, the standard (>)
requires that the left and the right parameters belong to the same Type.
One walk around is to create a custom function (>)
that accepts a left MyType
and a right int
parameters directly:
type MyType(info) = member x.Info : int = infolet inline (>) (left: MyType) (right: int) = left.Info > right let inline (<) (left: MyType) (right: int) = left.Info < right// see https://stackoverflow.com/questions/19682432/global-operator-overloading-in-f let inline (>) (left) (right) = match (box left, box right) with | (:? MyType as l, :? int as r ) -> l.Info > int right | (:? IComparable as left', :? IComparable )-> let r = left'.CompareTo right r > 0 | _ -> failwith "not support type " let inline (<) (left) (right) = match (box left, box right) with | (:? MyType as l, :? int as r ) -> l.Info < int right | (:? IComparable as left', :? IComparable )-> let r = left'.CompareTo right r < 0 | _ -> failwith "not support type "
Upvotes: 4
Reputation: 5005
Note that there is also a warning emitted at the declaration of >
:
The name '(>)' should not be used as a member name. To define comparison semantics for a type, implement the 'System.IComparable' interface. If defining a static member for use from other CLI languages then use the name 'op_GreaterThan' instead.
As itminus' answer shows, this is because >
is defined on two 'T
s that satisfy the comparable
constraint.
There is no operator overloading in F# like you can do in C#. This is because operators are functions, and functions cannot be overloaded.
So your choices are either to (a) implement System.IComparable
as the warning suggests, or (b) apply a workaround with inline
functions as itminus proposes.
Upvotes: 2