Chr*_*egg 7 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>
Run Code Online (Sandbox Code Playgroud)
现在我想从他们创建一个.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)
Run Code Online (Sandbox Code Playgroud)
虽然let bd有效let ad但无法编译,因为它无法推断类型并正确转换为BCL的Func.有趣的是,如果我省略第三个参数let ad2,或者如果我写出来fst并snd手动内联,它就可以正常工作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,但我仍然对它为什么不能用fst和snd函数编译感兴趣.
我认为这不是一个错误,而只是F#类型推理算法的一个非常难看的角落案例,其中涉及过载和类型定向转换.奇怪的是,编译器ad2正确地推断出绑定的类型,因为第二个重载ToDictionary具有两个参数,这会在推理期间导致额外的重载决策步骤.另一方面,只有一个带有三个参数的重载,因此在尝试推断ad类型时不使用重载决策步骤.
正如我所提到的,另一个难题是从F#函数到.NET委托类型的类型定向转换(这是你可以在Func<_,_>预期的情况下传递F#函数的方法).基本上,如果使用显式lambda或者存在多个重载,则会考虑此转换,但如果只有一个重载且没有显式lambda,则不考虑转换.这意味着以下内容也适用:
let ad3 = a.ToDictionary(System.Func<_,_>(fst),
System.Func<_,_>(snd), HashIdentity.Structural)
Run Code Online (Sandbox Code Playgroud)
因为现在不需要执行类型定向转换.
这个结果肯定是违反直觉的,所以我希望有一些方法可以调整类型推断算法来更好地处理这些极端情况.不幸的是,与.NET类型系统的某些方面(例如命名委托类型,子类型,重载等)的互操作使得推理比其他情况更难,并且可能有某些原因该算法不容易修改以处理这种情况.