【问题标题】:Delegate/Func conversion and misleading compiler error messageDelegate/Func 转换和误导性编译器错误消息
【发布时间】:2014-03-25 00:14:00
【问题描述】:

我认为 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 

最后一行编译失败,报错:

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

显然函数f 没有'a &gt; obj 的签名。如果 F# 3.1 编译器对第一个字典赋值感到满意,那么为什么不对第二个赋值呢?

【问题讨论】:

  • 我猜这是一种转换不是自动的情况,但错误不是很有帮助,但在技术上可能仍然是正确的。
  • 我猜只有 lambda 会自动转换。
  • @MisterMetaphor:正确:stackoverflow.com/questions/3392000/…
  • 但是,等等,为什么ffun (x:obj) -&gt; x 的类型不一样?升级到f : 'a -&gt; obj 是怎么回事?

标签: f#


【解决方案1】:

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

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

编辑

在查看过去与 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" 

【讨论】:

  • 不错。显然,规范曾经明确表示只有 lambda 表达式被如此提升;见this answer。我想知道现在转换的“其他函数值参数”可能是什么?
  • 编译器执行此操作的部分似乎是this bit。不过,我很难弄清楚“不是 lambda”情况下会发生什么。有人可以解释这里发生了什么吗?除了 lambda 之外,还有什么东西是自动转换的?
  • F# language suggestion 来改进自动转换,使其也适用于没有候选方法的情况。
【解决方案2】:

编辑:这个答案只解释了对'a -&gt; obj 的神秘提升。 @kvb 指出在 OP 示例中用 int 替换 obj 仍然 不起作用,因此促销本身不足以解释观察到的行为。


为了提高灵活性,F# 类型的阐述器可能在某些条件下将命名函数从 f : SomeType -&gt; OtherType 提升到 f&lt;'a where 'a :&gt; SomeType&gt; : 'a -&gt; OtherType。这是为了减少向上转换的需要。 (见spec. 14.4.2。)

第2题:

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

因为f 是一个“命名函数”,所以它的类型是从f : obj -&gt; obj 提升的。 14.4.2 到看似限制较少的f&lt;'a where 'a :&gt; obj&gt; : 'a -&gt; obj。但是这种类型与System.Func&lt;obj, obj&gt;不兼容。

问题一:

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

这很好,因为匿名函数没有命名,所以秒。 14.4.2 不适用。该类型永远不会从 obj -&gt; 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:it@135-31>       (* f promoted when used. *)

(解释器不会将约束输出到obj。)

【讨论】:

  • 这是不对的;不涉及自动升级(尝试相同的示例,但功能从 intint)。
猜你喜欢
  • 1970-01-01
  • 2012-02-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-17
相关资源
最近更新 更多