f#func类型的推理差异和pseq todictionary之间的差异



给定了一个小元素的序列,而使用f#powerpack的pseq的平行序列:

let a = Seq.singleton (1,"a")  --> val a: seq<int * string>
let b = a |> PSeq.map id       --> val b: pseq<int * string>

现在我想从它们创建.NET BCL字典:

let bd = b.ToDictionary(fst, snd, HashIdentity.Structural)
let ad = a.ToDictionary(fst, snd, HashIdentity.Structural)
let ad2 = a.ToDictionary(fst, snd)
let ad3 = a.ToDictionary((fun (x,y) -> x), (fun (x,y) -> y), HashIdentity.Structural)

虽然let bd起作用,但let ad不会编译,因为它无法正确推断类型并正确转换为BCL的Func。有趣的是,如果我省略了第三个参数,例如在let ad2中,或者我写出fstsndlet ad3中,它可以正常工作。

此表达式预计将具有type func&lt;(int * string),'a>,但这里有'b *'c->'b

  • 为什么let ad不编译,而所有替代方案都可以正常运行?
  • 有没有一种方法来制作let ad编译,而无需提供内联函数或类型注释?

ps:我需要哈希置。架构,因为在实际代码中,密钥不是整数,而是元组或记录

更新:我现在已经定义了let dictionary (s : ('a * 'b) seq) = s.ToDictionary((fun (x,y)->x), (fun (x,y)->y), HashIdentity.Structural),所以我可以写let ad = a |> dictionary,但是我仍然对为什么它不会与fstsnd函数编译。

我相信这不是一个错误,而只是一个非常丑陋的角案例,其中涉及过载和类型的定向转换的F#的类型推理算法。奇怪的是,编译器正确地注入ad2绑定的类型,因为ToDictionary的第二个过载具有两个参数,这会在推理过程中导致额外的过载分辨率步骤。另一方面,只有一个带有三个参数的过载,因此在尝试推断ad的类型时不会使用过载分辨率步骤。

正如我提到的,难题的另一部分是从f#函数到.NET代表类型的类型转换(这是您可以通过预期Func<_,_>的F#函数的方式)。基本上,如果您使用明确的lambda或存在多个过载,则考虑使用此转换,但是如果只有一个过载并且没有明确的lambda,则不考虑转换。这意味着以下内容也将起作用:

let ad3 = a.ToDictionary(System.Func<_,_>(fst), 
                         System.Func<_,_>(snd), HashIdentity.Structural)

因为现在不需要执行类型的定向转换。

这个结果当然是违反直觉的,因此我希望有一些方法可以调整类型的推理算法以更好地处理这些角案例。不幸的是,与.NET类型系统的某些方面进行互操作(例如命名的委托类型,子类型,超载等)使推理比其他可能性要困难得多,并且可能有某种原因可以使算法无法轻易成为修改以处理这种情况。

看起来像一个错误(或过载分辨率边缘案例),但是您可以使用内置的dict函数避免问题:

let a = Seq.singleton (1,"a")  
let b = a |> PSeq.map id 
let bd = dict b
let ad = dict a

最新更新