考虑一下:
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#代码干净无噪音的所有炒作是什么?
不知何故感觉这不可能是真的。 所以问题是:我错过了什么吗?我如何使这既紧凑又有效?
答案 0 :(得分:10)
类型推理和子类型不能很好地结合在一起,因为Carsten的链接在某种程度上进行了讨论。听起来你对F#的方法不满意,如果
,我会更喜欢它if b then
e1
else
e2
被隐含地视为更像
if b then (e1 :> 'a) else (e2 :> 'a)
使用编译器另外推断'a
是类型层次结构中的最小上限,基于否则将推断e1
和e2
的类型。
技术上可能会这样做,我无法明确地说出为什么F#不能以这种方式工作,但是这里有一个猜测:如果if
语句以这种方式表现,那么它永远不会是错误在if
和else
分支中具有不同类型,因为它们可以通过隐式向上转换为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。