在我的代码中,我想让F#自动将一种函数类型(例如IBase -> unit
)的值上载到另一种明确指定的函数类型(IDerived -> unit
)。虽然这似乎普遍得到支持,但我发现在没有明显原因的情况下失败。
在下面的代码中,case1
至case8
似乎与我等效:在每种情况下,右侧都有一个表达式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上提出问题,但感觉到问题很可能只是我的误解。
答案 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
被忽略)。真正的问题是解释的严格性。尝试使用此代码,您将看到警告,指出了这一点。