Пользовательское равенство типов со статически разрешенными параметрами типов - PullRequest
3 голосов
/ 13 мая 2019

Как реализовать пользовательские методы равенства в типах F # со статически разрешенными параметрами типа?

Я пытался сделать это следующим образом:

[<CustomEqualityAttribute>]
type Fraction< ^a when ^a: equality and ^a : (static member (*): ^a * ^a -> ^a) > (nominator: ^a, denominator: ^a) =
    member inline this.Nominator = nominator
    member inline this.Denominator = denominator

    member inline this.IsEqualTo(other: Fraction< ^a >) = this.Nominator * other.Denominator = other.Nominator * this.Denominator

    override inline this.Equals(other: obj) =
        match obj with
        | :? Fraction< ^a > as f -> this.IsEqualTo(f)
        | _ -> false

Я получаю следующую ошибку на this.Equals строка:

Этот элемент, функция или объявление значения не могут быть объявлены как «встроенные»

Почему это так?Это потому что Equals является переопределением?Если это так, есть ли способ реализовать пользовательское равенство или я вынужден использовать IEqualityComparer?

1 Ответ

3 голосов
/ 14 мая 2019

Почему это? Это потому что Equals является переопределением?

Да, это так. inline метод класса не является фактическим методом класса. Скорее, каждый вызов этого метода где-то будет интерпретироваться как его реализация (очень похоже на C ++). Поскольку вы переопределяете метод Equals, который является фактическим методом (из класса Object), вы не можете сделать его inline.

Если это так, можно ли вообще реализовать пользовательское равенство?

Вы можете извлечь конкретное умножение из типа, поэтому вам не придется использовать inline для метода Equals:

[<CustomEquality; NoComparison>]
type Frac<'T when 'T : equality> = private {
    nominator : 'T
    denominator : 'T
    mult : 'T -> 'T -> 'T
} with
    member x.Nominator = x.nominator
    member x.Denominator = x.denominator
    override x.Equals other =
        match other with
        | :? Frac<'T> as o -> 
            let ( * ) = x.mult in x.nominator * o.denominator = o.nominator * x.denominator
        | _ -> false
    static member inline Create x y = { nominator = x; denominator = y; mult = ( * ) }
// Test
Frac.Create 1 2 = Frac.Create 3 6 // true
Frac.Create 1.0 2.0 = Frac.Create 3.0 7.0 // false
Frac.Create 1 2 = Frac.Create 3.0 6.0 // compile error
...