为什么F#需要ToDictionary的类型占位符?

Mas*_*low 6 f# overload-resolution

特定

[
    1,"test2"
    3,"test"
]
|> dict
// turn it into keyvaluepair sequence
|> Seq.map id

|> fun x -> x.ToDictionary<_,_,_>((fun x -> x.Key), fun x -> x.Value)
Run Code Online (Sandbox Code Playgroud)

如果我没有明确使用<_,_,_>after ,则无法编译ToDictionary.
Intellisense工作正常,但编译失败并出现错误:基于此程序点之前的信息查找不确定类型的对象 因此,似乎Intellisense知道如何解决方法调用.

这似乎是一个线索

|> fun x -> x.ToDictionary<_,_>((fun x -> x.Key), fun x -> x.Value)
Run Code Online (Sandbox Code Playgroud)

失败了

Type constraint mismatch.  
The type 'b -> 'c  is not compatible with type IEqualityComparer<'a>     
The type 'b -> 'c' is not compatible with the type 'IEqualityComparer<'a>'  
(using external F# compiler)
Run Code Online (Sandbox Code Playgroud)

x.ToDictionary((fun x -> x.Key), id)

按预期工作

let vMap (item:KeyValuePair<_,_>) = item.Value
x.ToDictionary((fun x -> x.Key), vMap)
Run Code Online (Sandbox Code Playgroud)

我已经复制了FSI和LinqPad中的行为.

作为Eric Lippert的狂热读者,我真的想知道什么是重载决议,(或者可能来自不同地方的扩展方法)在这里与编译器混淆的冲突?

Ast*_*sti 2

尽管提前知道类型,但编译器会在采用元素选择器和比较器的重载之间感到困惑。lambda 编译FSharpFunc为 C# 中的标准委托类型(如Actionor )Func,并且从一种类型转换为另一种类型时确实会出现问题。为了使其发挥作用,您可以:

为有问题的 Func 提供类型注释

fun x -> x.ToDictionary((fun pair -> pair.Key), (fun (pair : KeyValuePair<_, _>) -> pair.Value)) //compiles
Run Code Online (Sandbox Code Playgroud)

或将参数命名为提示

fun x -> x.ToDictionary((fun pair -> pair.Key), elementSelector = (fun (pair) -> pair.Value))
Run Code Online (Sandbox Code Playgroud)

或者强制它选择 3 个参数的版本:

x.ToLookup((fun pair -> pair.Key), (fun (pair) -> pair.Value), EqualityComparer.Default)
Run Code Online (Sandbox Code Playgroud)

在旁边

在你的例子中,

let vMap (item:KeyValuePair<_,_>) = item.Value
x.ToDictionary((fun x -> x.Key), vMap)
Run Code Online (Sandbox Code Playgroud)

您将明确需要注释,vMap因为编译器无法在没有另一遍的情况下找出该属性存在的类型。例如,

List.map (fun x -> x.Length) ["one"; "two"] // this fails to compile
Run Code Online (Sandbox Code Playgroud)

这是管道运算符如此有用的原因之一,因为它允许您避免类型注释:

["one"; "two"] |> List.map (fun x -> x.Length) // works

List.map (fun (x:string) -> x.Length) ["one"; "two"] //also works
Run Code Online (Sandbox Code Playgroud)