F#Func类型推断Seq和PSeq ToDictionary之间的区别

时间:2012-10-17 11:33:54

标签: f# f#-3.0 f#-powerpack

给出一个简单的元组序列,以及使用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),或者我在fst中手动内联sndlet ad3,那么它的工作正常。

  

预计此表达式的类型为Func&lt;(int * string),'a&gt;但这里有'b *'c - &gt;类型“B

  • 为什么let ad无法编译,而所有替代方案都可以正常工作?
  • 有没有办法让let ad编译,而不提供内联函数或输入注释?

PS:我需要HashIdentity.Structural,因为在实际代码中,键不是整数,而是元组或记录

更新:我现在定义了let dictionary (s : ('a * 'b) seq) = s.ToDictionary((fun (x,y)->x), (fun (x,y)->y), HashIdentity.Structural)所以我可以写let ad = a |> dictionary,但我仍然对它为什么不能用fst和{{1进行编译感兴趣函数。

2 个答案:

答案 0 :(得分:9)

我认为这不是一个错误,而只是F#类型推理算法的一个非常难看的角落案例,其中涉及过载和类型定向转换。奇怪的是,编译器正确地推断出ad2绑定的类型,因为ToDictionary的第二个重载具有两个参数,这会在推理期间导致额外的重载决策步骤。另一方面,只有一个带有三个参数的重载,因此在尝试推断ad的类型时不会使用重载决策步骤。

正如我所提到的,另一个难题是从F#函数到.NET委托类型的类型导向转换(这是你可以传递一个F#函数,其中Func<_,_>是预期的)。基本上,如果使用显式lambda或者存在多个重载,则考虑此转换,但如果只有一个重载且没有显式lambda,则不考虑转换。这意味着以下内容也可以使用:

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

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

这个结果肯定是违反直觉的,所以我希望有一些方法来调整类型推断算法以更好地处理这些极端情况。不幸的是,与.NET类型系统的某些方面(例如命名委托类型,子类型,重载等)的互操作使得推理比其他情况更难,并且可能有某些原因该算法不容易修改以处理这种情况。

答案 1 :(得分:2)

看起来像是一个错误(或重载解析边缘情况),但您可以使用内置的dict函数来避免此问题:

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