错误的FS0064警告?此构造使代码不如类型注释所指示的那样通用

时间:2018-01-30 22:45:29

标签: f#

我正在尝试创建一个实现.NET IComparer接口的类,以便与各种System.Collections.Generic集合一起使用。此IComparer将使用外部提供的函数将集合的值映射到排序键。

示例应用程序可能是SortedSet<City>,按人口排序,但是从外部数据源动态检索群体,该外部数据源是单独维护和更新的。

open System
open System.Collections.Generic

type ExternalComparer<'T>(compareBy: ('T -> 'U) when 'U :> IComparable) =
    let compareBy = compareBy

    interface IComparer<'T> with
        member this.Compare(a, b) =
            let x = compareBy a
            let y = compareBy b
            if x < y then -1 else if x > y then 1 else 0

编译器在if x < y表达式上发出以下警告,特别是在x上:

Warning FS0064  This construct causes code to be less generic than 
indicated by the type annotations. The type variable 'U has been 
constrained to be type 'IComparable'.

实际上,我打算将类型变量'U约束为类型'IComparable',并且我试图在when 'U :> IComparable函数的定义中用compareBy约束来表达该意图类型。

这条警告信息是错误的,还是我做错了什么?

3 个答案:

答案 0 :(得分:2)

我认为您的解决方案 - 只接受ne_chunk类型的函数是正确的,即使这意味着'T -> IComparable的用户可能需要插入一个upcast来制作代码编译。

为了解释您收到错误的原因,您的类只有一个泛型类型参数ExternalComparer,并且缺少'T参数 - 因此编译器将其约束为'U。如果你想解决这个问题,你必须添加另一个通用参数:

IComparable

此外,我还必须添加type ExternalComparer<'T, 'U when 'U :> IComparable>(compareBy: ('T -> 'U)) = let compareBy = compareBy interface IComparer<'T> with member this.Compare(a, b) = let x = compareBy a :> IComparable let y = compareBy b :> IComparable if x < y then -1 else if x > y then 1 else 0 ,以便将结果从:> IComparable转换为可以比较的'U。这增加了另一个通用参数,这非常愚蠢。

如果您想避免这种情况,可以使用将IComparable函数转换为
的静态成员 'T -> 'U函数在传递给构造函数之前的函数:

'T -> IComparable

现在您可以很好地使用type ExternalComparer<'T>(compareBy: ('T -> IComparable)) = let compareBy = compareBy interface IComparer<'T> with member this.Compare(a, b) = let x = compareBy a let y = compareBy b if x < y then -1 else if x > y then 1 else 0 static member Create(compareBy : 'T -> #IComparable) = ExternalComparer(fun v -> compareBy v :> IComparable) 方法:

Create

答案 1 :(得分:1)

问题在于,您定义compareBy类型以返回'U,约束为IComparable,此时您可以更明确地将其定义为返回IComparable直接,像这样:

type ExternalComparer<'T>(compareBy: ('T -> IComparable)) =

由于您知道'U始终是IComparable,因此使用泛型类型不正确。

答案 2 :(得分:1)

在我看来,你可以将一大块代码折叠成一个对象表达式:

let bigCity = {Name="NYC"; population = 1_000_000}
let smallCity = {Name="KC"; population = 1000}

let cityComparer compFunc = { new System.Collections.Generic.IComparer<'T> with 
                                override __.Compare(c1, c2) = 
                                    let x = compFunc c1
                                    let y = compFunc c2
                                    if x < y then -1 else if x > y then 1 else 0
}

let comp = cityComparer (fun x -> x.population)
comp.Compare(smallCity,bigCity)
//val it : int = -1