7

给定一个简单的元组序列,以及一个使用 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。有趣的是,如果我省略第三个参数(如 in let ad2,或者如果我写出fstsnd手动内联in ),它工作得很好let ad3

这个表达式应该有类型 Func<(int * string),'a> 但这里有类型 'b * 'c -> '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,但我仍然对它为什么不能用fstandsnd函数编译感兴趣。

4

2 回答 2

9

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

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

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

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

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

于 2012-10-17T14:49:56.470 回答
2

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

let a = Seq.singleton (1,"a")  
let b = a |> PSeq.map id 
let bd = dict b
let ad = dict a
于 2012-10-17T14:17:10.460 回答