自动上载功能的规则是什么?

时间:2019-01-25 20:48:46

标签: f#

在我的代码中,我想让F#自动将一种函数类型(例如IBase -> unit)的值上载到另一种明确指定的函数类型(IDerived -> unit)。虽然这似乎普遍得到支持,但我发现在没有明显原因的情况下失败。

在下面的代码中,case1case8似乎与我等效:在每种情况下,右侧都有一个表达式IBase -> unit绑定到名称输入IDerived -> unit。那么为什么不允许案例4、5和7?

type IBase = interface end
type IDerived = inherit IBase

let foo (x: IBase) = ()
let bar = fun (x: IBase) -> ()

type T = T with static member (!!) (_: T) = foo
let baz = !!T

let case1: IDerived -> unit = foo                               // OK
let case2: IDerived -> unit = bar                               // OK
let case3: IDerived -> unit = baz                               // OK
let case4: IDerived -> unit = !!T                               // Expecting 'IDerived -> unit' but given 'IBase -> unit'
let case5: IDerived -> unit = upcast !!T                        // Type 'IBase -> unit' is not compatible with type 'IDerived -> unit'
let case6: IDerived -> unit = let z = !!T in z                  // OK
let case7: IDerived -> unit = fun (x: IBase) -> ()              // Expected x to have type 'IDerived' but here has type 'IBase'
let case8: IDerived -> unit = let z = fun (x: IBase) -> () in z // OK

编辑:为澄清起见,我主要想知道为什么编译器没有平等地对待8种情况。例如,我希望let z...中的绑定case8是多余的,但会有所作为(与case7相比)。为什么?

编辑:以下代码演示了我要实现的目标:https://dotnetfiddle.net/AlpdpO它还包含一个解决方案/解决方法,所以我实际上是在要求更多的技术细节/原因而不是替代方法。我曾考虑过在GitHub上提出问题,但感觉到问题很可能只是我的误解。

1 个答案:

答案 0 :(得分:2)

F#的类型推断似乎不够聪明,无法处理其中一些情况,但是您可以通过使定义更明确一些来帮助解决问题。试试这个:

type IBase = interface end
type IDerived = inherit IBase

let foo (x: IBase) = ()

type T = T with static member (!!) (_: T) : #IBase -> unit = foo

let case4: IDerived -> unit = !!T
let case5: IDerived -> unit = upcast !!T
let case7: IDerived -> unit = fun (x: #IBase) -> ()

...,当然,您最初通过的案件也会继续通过。问题是F#对于(!!)函数的类型和lambda的类型过于严格,因此我们可以通过显示“ IBase或任何其他类型的显式类型注释来帮助它它的派生词在这里很好。”

总而言之,请为这些句法案例提交一个错误。对于编译器,在类型方面要谨慎一点,这并不是一件很糟糕的事情,就像F#编译器在这里所​​做的那样。如果太宽容,可能会发生更糟糕的事情。在这里,最糟糕的结果是您必须为此做一些打字工作。

我应该指出,这确实与强制转换无关,也没有强制转换({upcast被忽略)。真正的问题是解释的严格性。尝试使用此代码,您将看到警告,指出了这一点。