Delegate/Func conversion and misleading compiler error message

心不动则不痛 提交于 2020-01-10 19:35:31

问题


I thought that conversions between F# functions and System.Func had to be done manually, but there appears to be a case where the compiler (sometimes) does it for you. And when it goes wrong the error message isn't accurate:

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 

The last line fails to compile, and the error is:

This expression was expected to have type
    System.Func<obj,obj>    
but here has type
    'a -> obj

Clearly the function f doesn't have a signature of 'a > obj. If the F# 3.1 compiler is happy with the first dictionary assignment, then why not the second?


回答1:


The part of the spec that should explain this is 8.13.7 Type Directed Conversions at Member Invocations. In short, when invoking a member, an automatic conversion from an F# function to a delegate will be applied. Unfortunately, the spec is a bit unclear; from the wording it seems that this conversion might apply to any function expression, but in practice it only appears to apply to anonymous function expressions.

The spec is also a bit out of date; in F# 3.0 type directed conversions also enable a conversion to a System.Linq.Expressions.Expression<SomeDelegateType>.

EDIT

In looking at some past correspondence with the F# team, I think I've tracked down how a conversion could get applied to a non-syntactic function expression. I'll include it here for completeness, but it's a bit of a strange corner case, so for most purposes you should probably consider the rule to be that only syntactic functions will have the type directed conversion applied.

The exception is that overload resolution can result in converting an arbitrary expression of function type; this is partly explained by section 14.4 Method Application Resolution, although it's pretty dense and still not entirely clear. Basically, the argument expressions are only elaborated when there are multiple overloads; when there's just a single candidate method, the argument types are asserted against the unelaborated arguments (note: it's not obvious that this should actually matter in terms of whether the conversion is applicable, but it does matter empirically). Here's an example demonstrating this exception:

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" 



回答2:


EDIT: This answer explains only the mysterious promotion to 'a -> obj. @kvb points out that replacing obj with int in OPs example still doesn't work, so that promotion is in itself insufficient explanation for the observed behaviour.


To increase flexibility, the F# type elaborator may under certain conditions promote a named function from f : SomeType -> OtherType to f<'a where 'a :> SomeType> : 'a -> OtherType. This is to reduce the need for upcasts. (See spec. 14.4.2.)

Question 2 first:

dict["bar"] <- f                     (* Why does this fail? *)

Because f is a "named function", its type is promoted from f : obj -> obj following sec. 14.4.2 to the seemingly less restrictive f<'a where 'a :> obj> : 'a -> obj. But this type is incompatible with System.Func<obj, obj>.

Question 1:

dict["foo"] <- fun (x:obj) -> x      (* Why doesn't this, then?  *)

This is fine because the anonymous function is not named, and so sec. 14.4.2 does not apply. The type is never promoted from obj -> obj and so fits.


We can observe the interpreter exhibit behaviour following 14.4.2:

> let f = id : obj -> obj
val f : (obj -> obj)                         (* Ok, f has type obj -> obj *)
> f
val it : ('a -> obj) = <fun:it@135-31>       (* f promoted when used. *)

(The interpreter doesn't output constraints to obj.)



来源:https://stackoverflow.com/questions/22606610/delegate-func-conversion-and-misleading-compiler-error-message

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