Пользовательские операторы сравнения F # для типа - PullRequest
2 голосов
/ 10 июля 2019

Я хочу создать пользовательские операторы сравнения для моего типа (<,>, <=,> = и т. Д.).Я попытался:

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 ]

3 голосов
/ 10 июля 2019

Причина, по которой вы получаете такую ​​ошибку

Я не знаю, почему 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, стандарт (>) требует, чтобы левый и правый параметры принадлежали к одному и тому же типу.

A Walkaround

Обходной путь - создать пользовательскую функцию (>), которая принимает параметры MyType влево и * int справа:

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

<strike>
let inline (>) (left: MyType) (right: int) = 
    left.Info > right

let inline (<) (left: MyType) (right: int) = 
    left.Info < right
</strike>

// 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 голос
/ 10 июля 2019

Обратите внимание, что при объявлении >:

также выдается предупреждение.

Имя '(>)' не должно использоваться в качестве имени члена. Чтобы определить семантику сравнения для типа, реализуйте интерфейс System.IComparable. Если вы определяете статический член для использования в других языках CLI, используйте вместо этого имя op_GreaterThan.

Как показывает ответ itminus, это потому, что > определено на двух 'T s, которые удовлетворяют ограничению comparable.

В F # нет перегрузки операторов, как в C #. Это связано с тем, что операторы являются функциями, а функции не могут быть перегружены.

Таким образом, вы можете либо (a) реализовать System.IComparable, как следует из предупреждения, либо (b) применить обходной путь с inline функциями, как это предлагает itminus.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...