为什么不在f#中自动向上转换

时间:2013-05-29 07:23:46

标签: casting f#

我在F#中实现了一个类似的C#接口:

public interface IThings
{
    Stream ThingsInAStream()
}

我的实现看起来像:

type FSharpThings() = 
    interface IThings with
       member this.ThingsInAStream() = 
           let ms = new MemoryStream()
           // add things to stream
           ms

现在我收到消息:

The expression was expected to have type 
  Stream
but here has type
  MemoryStream

我不明白MemoryStream IS 一个Stream我知道我可以把它像流一样投射到:

ms :> Stream

同样适用于[|"string"|]IEnumerable<string>它实现了接口,我可以显式地转换为它但它不能自动工作。

为什么这样做?

let things:(IEnumerable<'a> -> 'a) = (fun f -> f.First())

let thing= things([|"";""|])

这也是自动上传!

2 个答案:

答案 0 :(得分:12)

我认为尼古拉斯的答案一般都是正确的。允许在语言中自动向上到处会导致类型推断出现问题。

原则上,编译器可以尝试寻找在不同分支中返回的类型的公共基类型,但这并不像听起来那么容易:

  • 首先,它应该返回最具体的类型还是其他类型? (编译器可以找到最具体的,但实际上你可能想要返回比你的代码推断的更通用的东西 - 所以明确指定它是有用的。)

  • 其次,界面变得困难。想象一下,两个分支返回两个不同的类,它们都实现接口IAIB。编译器如何确定返回类型应该是IA还是IB,还是obj? (这是一个大问题,因为它会显着影响代码的使用方式!)有关详细信息,请参阅this snippet

但是,有一个地方这不是问题,F#编译器允许它。也就是说,当将参数传递给函数或方法时 - 在这种情况下,编译器知道所需的类型是什么,因此它只需要检查允许upcast;它不需要推断要插入的upcast。因此,类型推断不受影响,因此编译器可以插入upcast。这就是为什么以下工作:

// The example from the question
let first (items:seq<'a>) = items |> Seq.head
let thing = first [|"";""|]

// Even simpler example - passing string as object
let foo (a:obj) = a 
foo "123"

此处,参数为array<string>,函数需要seq<string>。编译器知道要插入的upcast(因为它知道目标类型),所以它就是这样做的。

答案 1 :(得分:11)

这是具有强大的类型推断机制的对手方: 通过明确所有内容,编译器可以更容易来推断出真实与否。

起初感觉很奇怪,因为我们非常依赖其他轻松语言中的转换。

但实际上它总体上是一种强度,并允许所提到的类型推断,以及促进良好的编程实践,如编程到接口VS具体实现。

在案例中,一个有用的构造,它感觉简单多余,是添加演员

//The cast will be determined by the compiler, because of _
result :> _