为什么不进行类型推断会自动确定该类型存在的属性?

时间:2017-02-22 13:52:28

标签: f# type-inference

我需要比较不同类型的对象,我知道它们都具有属性Id和Legacy_id。不幸的是我无法为它们添加接口,因为类型来自数据库模式。我希望下面的比较器可以工作:

type Comparer<'T >()=
  interface System.Collections.Generic.IEqualityComparer<'T> with 
    member this.Equals (o1:'T,o2:'T)=
      o1.Legacy_id=o2.Legacy_id
    member this.GetHashCode(o:'T)=
      o.Id+o.Legacy_id

此外,我还有类型的比较器类型的实例化。所以,理论上编译器有足够的信息。

但是它给出了一个错误:&#34;根据此程序点之前的信息查找不确定类型的对象。在此程序点之前可能需要类型注释来约束对象的类型。这可能允许解析查找。&#34;

我想知道为什么F#在这里失败了?是否有任何实际/理论限制或者它没有实施?这种推断可能非常有用。

我怀疑解释是关于F#编译器只是向前走。 C#的限制并不存在。那是什么错误消息在抱怨。是这样吗?

2 个答案:

答案 0 :(得分:6)

成员约束不能用于类型,这就是为什么你不能这样做。请参阅here

你可以做的是创建一个比较器类,它接受特定类型的显式等式检查和哈希码生成函数,例如

type Comparer<'T>(equalityFunc, hashFunc) =
  interface System.Collections.Generic.IEqualityComparer<'T> with 
    member this.Equals (o1:'T,o2:'T)=
      equalityFunc o1 o2
    member this.GetHashCode(o:'T)=
      hashFunc o

然后,您可以使用内联函数为与您希望强加的约束匹配的类型生成上述实例:

let inline id obj =
    ( ^T : (member Id : int) (obj))

let inline legacyId obj =
    ( ^T : (member Legacy_id : int) (obj))

let inline equals o1 o2 =
    id o1 = id o2

let inline hash o =
    id o + legacyId o

let inline createComparer< ^T when ^T : (member Id: int) and ^T : (member Legacy_id : int) >() =
    Comparer< ^T >(equals, hash) :> System.Collections.Generic.IEqualityComparer< ^T >

假设您有一些类型TestType具有两个必需属性:

type TestType =
   member this.Legacy_id = 7
   member this.Id = 9

然后,您可以执行createComparer<TestType>()生成适合您的类型的相等比较器。

答案 1 :(得分:5)

获取比较器的简明方法,例如创建HashSet是:

let inline getId o = (^T : (member Id : int) o)
let inline getLegacyId o = (^T : (member Legacy_id : int) o)

let inline legacyComparer< ^T when ^T : (member Id : int) and ^T : (member Legacy_id : int)>() =
    { new System.Collections.Generic.IEqualityComparer<'T> with
          member __.GetHashCode o = getId o + getLegacyId o
          member __.Equals(o1, o2) = getLegacyId o1 = getLegacyId o2 }

类型推断是关于推断 a 类型,而不是证明某些泛型类型参数满足所有实例化的任意条件(参见名义与结构类型系统)。 SO上的静态解析类型参数有很多好的答案。