如何使F#推断共同基类型?

时间:2012-03-27 15:56:16

标签: visual-studio-2010 inheritance f# type-inference

考虑一下:

module Module1 =
    type A() = class end
    type B() = inherit A()
    type C() = inherit A()

    let f x = if x > 0 then new B() else new C()

最后一行产生关于预期类型B的错误,但是找到了类型C. 好吧,我可以假装理解:编译器不知道在有多少公共基础的情况下推断。

但是猜猜怎么着?即使我指定了函数类型,它仍然不起作用:

    let f x : A = if x > 0 then new B() else new C()

现在这给了我两个错误:“ A预期,B找到”和“ A预期,C找到”。 WTF?为什么不能看到B和C都可以隐式转换为A?

是的,我知道我可以使用upcast,就像这样:

    let f x : A = if x > 0 then upcast new B() else upcast new C()

但猜猜(又一次)? upcast仅适用于显式函数类型声明的存在! 换句话说,这个:

    let f x = if x > 0 then upcast new B() else upcast new C()

仍然会出错。

WTF?为了帮助编译器,我是否真的必须在程序中添加50%的噪音? 关于F#代码干净无噪音的所有炒作是什么?

不知何故感觉这不可能是真的。 所以问题是:我错过了什么吗?我如何使这既紧凑又有效?

2 个答案:

答案 0 :(得分:10)

类型推理和子类型不能很好地结合在一起,因为Carsten的链接在某种程度上进行了讨论。听起来你对F#的方法不满意,如果

,我会更喜欢它
if b then 
    e1 
else 
    e2

被隐含地视为更像

if b then (e1 :> 'a) else (e2 :> 'a)

使用编译器另外推断'a是类型层次结构中的最小上限,基于否则将推断e1e2的类型。

技术上可能会这样做,我无法明确地说出为什么F#不能以这种方式工作,但是这里有一个猜测:如果if语句以这种方式表现,那么它永远不会是错误在ifelse分支中具有不同类型,因为它们可以通过隐式向上转换为obj来统一。但是,在实践中,这几乎总是程序员错误 - 你几乎总是希望类型相同(例如,如果我从一个分支返回一个字符而另一个字符串返回一个字符串,我可能意味着从两个字符串返回字符串,而不是{ {1}})。通过隐式向上转换,您只会更难找到这些错误。

此外,在F#中处理复杂的继承层次结构相对较少,除非与其他.NET代码进行互操作。结果,这在实践中是非常小的限制。如果您正在寻找比obj语法更短的解决方案,您可以尝试upcast,只要有限制类型的内容(整体结果的注释,或者其中一个分支的特定演员。)

答案 1 :(得分:2)

所有这一切都有一个原因,但要缩短:F#比C#更强类型,所以你必须告诉去哪里(见here):

let f x = if x > 0 then (new B() :> A) else (new C() :> A)

您可以在此处找到更多信息:F# need for cast

这是另一个伟大的discussion