F#类型的自定义比较运算符

时间:2019-07-10 01:41:36

标签: f#

我想为我的类型(<,>,<=,> =等)创建自定义比较运算符。我尝试过:

type MyType() =
    static member (>) (left: MyType, right: int) = true


let foo = new MyType();
let bar = foo > 12;

并收到错误:

The type 'MyType' does not support the 'comparison' constraint. For example, it does not support the 'System.IComparable' interface

(为什么像运算符重载这样的语言功能依赖于来自框架的接口IComparable?该语言及其使用的框架不应该独立吗?)所以我尝试:

type MyType() =
    interface IComparable
        member self.CompareTo yobj = true
    interface IComparable<int>
        member self.CompareTo yobj = true
    static member (>) (left: MyType, right: int) = true

我得到:

This expression was expected to have type 'MyType' but here has type 'int'

如何获得预期的行为?

2 个答案:

答案 0 :(得分:3)

出现此类错误的原因

我不知道为什么F#不允许我写类似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; 
    }
}

即使该类型未实现IComparable接口,我也可以将其与int进行如下比较:

t > 2
t < 6

似乎F#(>)视为T' -> T' -> bool

val ( > ): 
   x: 'T (requires comparison )->
   y: 'T (requires comparison )
   -> bool

这表明:

  1. 左右参数是相同的类型。
  2. T'需要进行比较(IComparable

如果我对它的理解正确,这就是为什么出现以下错误的原因:

  

该表达式的类型应为'MyType',但此处的类型应为'int'

即使您已经实现了IComparable接口,标准(>)仍要求left和right参数属于同一类型。

环游

一种解决方法是创建一个自定义函数 (>) ,该函数直接接受左侧的MyType和右侧的int参数:

type MyType(info)  = 
    member x.Info : int = info 


let 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 "

答案 1 :(得分:1)

请注意,>的声明中还会发出警告:

  

名称“(>)”不应用作成员名称。要定义类型的比较语义,请实现“ System.IComparable”接口。如果要定义静态成员以供其他CLI语言使用,请改用名称“ op_GreaterThan”。

正如它的答案所显示的,这是因为>是在满足'T约束的两个comparable上定义的。

F#中没有像C#中那样的运算符重载。这是因为运算符是函数,并且函数不能重载。

因此,您的选择是(a)根据警告的建议实施System.IComparable,或(b)根据建议使用inline函数的变通办法。