2014-03-24 66 views
7

我认为F#函数和System.Func之间的转换必须手动完成,但似乎有编译器(有时)会为您执行它的情况。而当它出现问题的错误消息是不准确的:代表/ 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 

最后一行编译失败,错误是:

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

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

+0

我想这是一个转换不是自动的情况,但这个错误并不是很有帮助,但可能在技术上仍然是正确的。 –

+0

我猜想只有lambda会自动转换。 – MisterMetaphor

+0

@MisterMetaphor:correct:http://stackoverflow.com/questions/3392000/interop-between-f-and-c-sharp-lambdas – Mau

回答

5

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

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

编辑

在寻找与F#团队过去的一些信件,我想我已经找到了一个转换如何能得到应用到非句法功能表达。为了完整性,我将它包含在这里,但这有点奇怪,所以对于大多数目的,您应该考虑规则是只有语法函数才会应用类型定向转换。

例外情况是重载解析可能导致转换函数类型的任意表达式;这部分由部分14.4 Method Application Resolution解释,虽然它很密集,但仍不完全清楚。基本上,当存在多个重载时,参数表达式只有详述了;当只有一个候选方法时,参数类型是针对未经讨论的论点而提出的(注意:这并不是显而易见的,就转换是否适用而言,这实际上很重要,但它在经验上确实很重要)。下面是一个演示这个例外的例子:

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" 
+1

不错。显然,规范专门用来说明只有lambda表达式才被提升;看到[这个答案](http://stackoverflow.com/questions/2783450/creating-delegates-with-lambda-expressions-in-f)。我想知道现在转换的“其他函数值参数”可能是什么? –

+0

执行此操作的编译器部分似乎是[this bit](https://github.com/fsharp/fsharp/blob/master/src/fsharp/typrelns.fs#L2202)。尽管如此,我在解决“不是lambda”的情况下遇到了什么困难。有人能解释这里发生了什么吗?除了自动转换的lambda外,还有其他的东西吗? –

+1

@SørenDebois - 看我的编辑。 – kvb

0

编辑:这个答案只解释了对'a -> obj的神秘促销。 @kvb指出,在OP的例子仍然替换objint不起作用,因此促进本身对观察到的行为本身的解释不足。


为了提高灵活性,F#的类型elaborator可以在一定条件下促进从f : SomeType -> OtherTypef<'a where 'a :> SomeType> : 'a -> OtherType命名函数。这是为了减少对upcasts的需求。 (参见spec. 14.4.2

问题2第一:

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

由于f是 “命名的函数”,它的类型是从f : obj -> obj以下秒促进。 14.4.2对表面上较少限制的f<'a where 'a :> obj> : 'a -> obj。但是这种类型与System.Func<obj, obj>不兼容。

问题1:

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

这是很好的,因为匿名函数没有命名,因此秒。 14.4.2不适用。这种类型从来没有从obj -> obj晋升,所以适合。


我们可以观察到的解释行为表现出以下14.4.2:

> let f = id : obj -> obj 
val f : (obj -> obj)       (* Ok, f has type obj -> obj *) 
> f 
val it : ('a -> obj) = <fun:[email protected]>  (* f promoted when used. *) 

(解释器不会输出约束obj

+2

这是不对的;不涉及自动提升(尝试相同的例子,但函数从'int'到'int')。 – kvb

+0

呵呵。我更新了答案。 –

相关问题