F# 错误的FS0064警告?此构造导致代码不像类型注释所指示的那样通用

F# 错误的FS0064警告?此构造导致代码不像类型注释所指示的那样通用,f#,F#,我正在尝试创建一个类,该类实现了一个通用的.NET IComparer接口,用于各种System.Collections.generic Collections。此IComparer将使用外部提供的函数将集合的值映射到排序键 示例应用程序可能是按填充进行排序的SortedSet,但填充是从单独维护和更新的外部数据源动态检索的 open System open System.Collections.Generic type ExternalComparer<'T>(compareBy

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

示例应用程序可能是按填充进行排序的
SortedSet
,但填充是从单独维护和更新的外部数据源动态检索的

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
事实上,我打算将类型变量“U”约束为“IComparable”类型,并且我试图在函数类型定义中用
compareBy
时的
when“U:>IComparable
约束来表达这一意图


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

问题是您正在定义
compareBy
类型以返回一个
'U
,约束为
IComparable
,而您可以更清楚地定义它以直接返回一个
IComparable
,如下所示:

type ExternalComparer<'T>(compareBy: ('T -> IComparable)) =
类型外部比较器(可比较))=

由于您知道
'U
始终是一个
i可比较的
,因此泛型类型的使用是不正确的。

问题在于,您正在通过
类型定义
比较,以返回一个
'U
,并将其约束为
i可比较的
,当您可以更清楚地定义它以直接返回一个
IComparable
时,如下所示:

type ExternalComparer<'T>(compareBy: ('T -> IComparable)) =
类型外部比较器(可比较))=

因为你知道,U
永远是一个
I可比较的
,所以泛型类型的使用是不正确的。

我认为你的解决方案-只要接受类型为
'T->I可比较的
的函数就是正确的选择,即使这意味着
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
现在您可以很好地使用
Create
方法:

ExternalComparer<string>(fun v -> int v :> _) // Requires upcast
ExternalComparer<string>.Create(fun v -> int v) // Works directly!
ExternalComparer(fun v->int v:>\ux)//需要上溯
Create(fun v->int v)//直接工作!

我认为您的解决方案-只接受类型为
'T->IComparable
的函数是正确的,即使这意味着
外部比较器的用户可能需要插入一个向上转换来编译代码

为了解释出现错误的原因,您的类只有一个泛型类型参数
'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
现在您可以很好地使用
Create
方法:

ExternalComparer<string>(fun v -> int v :> _) // Requires upcast
ExternalComparer<string>.Create(fun v -> int v) // Works directly!
ExternalComparer(fun v->int v:>\ux)//需要上溯
Create(fun v->int v)//直接工作!

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

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
let bigCity={Name=“NYC”;population=1_000_000}
设smallCity={Name=“KC”;population=1000}

让cityComparer compFunc={new System.Collections.Generic.IComparer在我看来,您可以将一段代码折叠成一个对象表达式:

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
let bigCity={Name=“NYC”;population=1_000_000}
设smallCity={Name=“KC”;population=1000}
让cityComparer compFunc={new System.Collections.Generic.IComparer