委托/ Func转换和误导性编译器错误消息

Aka*_*ash 7 f#

我认为F#函数和System.Func之间的转换必须手动完成,但似乎有一种情况,编译器(有时)会为你做.当出错时,错误信息不准确:

module Foo =
    let dict = new System.Collections.Generic.Dictionary<string, System.Func<obj,obj>>()

    let f (x:obj) = x

    do
        // Question 1: why does this compile without explicit type conversion?
        dict.["foo"] <- fun (x:obj) -> x 
        // Question 2: given that the above line compiles, why does this fail?
        dict.["bar"] <- f 
Run Code Online (Sandbox Code Playgroud)

最后一行无法编译,错误是:

This expression was expected to have type
    System.Func<obj,obj>    
but here has type
    'a -> obj
Run Code Online (Sandbox Code Playgroud)

显然,该功能f没有签名'a > obj.如果F#3.1编译器对第一个字典赋值感到满意,那为什么不是第二个呢?

kvb*_*kvb 5

应该解释这一点的规范的一部分是8.13.7在成员调用中的类型定向转换.简而言之,在调用成员时,将应用从F#函数到委托的自动转换.不幸的是,规范有点不清楚; 从措辞看来,这种转换可能适用于任何函数表达式,但实际上它似乎只适用于匿名函数表达式.

规格也有点过时了; 在F#3.0类型定向转换中也可以转换为System.Linq.Expressions.Expression<SomeDelegateType>.

编辑

在查看过去与F#团队的一些通信时,我想我已经跟踪了转换如何应用于非语法函数表达式.为了完整起见,我将它包括在这里,但这是一个奇怪的角落情况,因此对于大多数目的,您应该考虑规则是只有语法函数将应用类型定向转换.

例外是重载解析可能导致转换函数类型的任意表达式; 第14.4方法应用解决方案部分解释了这一点,尽管它非常密集且仍然不完全清楚.基本上,只有在存在多个重载时才会详细说明参数表达式; 当只有一个候选方法时,参数类型会根据无法解释的参数进行断言(注意:根据转换是否适用,这实际上并不重要,但它在经验上确实很重要).这是一个演示此异常的示例:

type T =
    static member M(i:int) = "first overload"
    static member M(f:System.Func<int,int>) = "second overload"

let f i = i + 1

T.M f |> printfn "%s" 
Run Code Online (Sandbox Code Playgroud)