F# Func type inference difference between Seq and PSeq ToDictionary

心已入冬 提交于 2019-12-01 15:27:41

问题


Given a trivial sequence of tuples, and a parallel one using F# PowerPack's PSeq:

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

Now I'd like to create a .Net BCL dictionary from them:

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)

While let bd works, let ad does not compile as it fails to infer types and convert to BCL's Func correctly. Interestingly it works just fine if I omit the third parameter, as in let ad2, or if I write out fst and snd manually inline as in let ad3.

This expression was expected to have type Func<(int * string),'a> but here has type 'b * 'c -> 'b

  • Why does let ad not compile, while all the alternatives work fine?
  • Is there a way to make let ad compile, without providing inline functions or type annotations?

PS: I need HashIdentity.Structural because in the real code the keys are not integers but tuples or records

Update: I've now defined let dictionary (s : ('a * 'b) seq) = s.ToDictionary((fun (x,y)->x), (fun (x,y)->y), HashIdentity.Structural) so I can just write let ad = a |> dictionary, but I'm still interested in why it won't compile with the fst and snd functions.


回答1:


I believe that this is not exactly a bug, but merely a very ugly corner case of F#'s type inference algorithm where overloads and type directed conversions are involved. Oddly, the compiler infers the ad2 binding's type correctly because there's a second overload of ToDictionary that has two arguments, which causes an additional overload resolution step during inference. On the other hand, there is only a single overload with three arguments, so the overload resolution step isn't used when trying to infer ad's type.

As I mentioned, the other piece of the puzzle is the type directed conversion from an F# function to a .NET delegate type (this is how you can pass an F# function where a Func<_,_> is expected). Basically, if you use an explicit lambda or if there are multiple overloads, then this conversion is considered, but if there is only a single overload and no explicit lambda then the conversion isn't considered. This means that the following will also work:

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

because now there's no need to perform a type directed conversion.

This result is certainly counterintuitive, so I'd hope that there is some way to tweak the type inference algorithm to better handle these corner cases. Unfortunately, interoperating with certain aspects of the .NET type system (such as named delegate types, subtyping, overloading, etc.) makes inference much harder than it might otherwise be, and there may be some reason that the algorithm can't easily be modified to handle this case.




回答2:


Looks like a bug (or an overload resolution edge case), but you can avoid the issue by using the built-in dict function:

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


来源:https://stackoverflow.com/questions/12933366/f-func-type-inference-difference-between-seq-and-pseq-todictionary

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!