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

alb*_*jan 14 f# casting

我在F#中实现了一个C#接口,它看起来像:

public interface IThings
{
    Stream ThingsInAStream()
}
Run Code Online (Sandbox Code Playgroud)

我的实现看起来像:

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

现在我收到消息:

The expression was expected to have type 
  Stream
but here has type
  MemoryStream
Run Code Online (Sandbox Code Playgroud)

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

ms :> Stream
Run Code Online (Sandbox Code Playgroud)

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

为什么这样做?

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

let thing= things([|"";""|])
Run Code Online (Sandbox Code Playgroud)

这也是自动向上转换!

Tom*_*cek 12

我认为Nicolas的答案一般都是正确的.允许在语言的任何地方自动向上转换会导致类型推断出现问题.

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

  • 首先,它应该返回最具体的类型还是其他类型?(编译器可以找到最具体的,但实际上您可能希望返回比从代码中推断的更通用的东西 - 因此明确指定它是有用的.)

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

但是,有一个地方这不是问题,F#编译器允许它.也就是说,当将参数传递给函数或方法时 - 在这种情况下,编译器知道所需的类型是什么,因此它只需要检查是否允许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"
Run Code Online (Sandbox Code Playgroud)

这里,参数是array<string>和函数所期望的seq<string>.编译器知道要插入的upcast(因为它知道目标类型),所以它就是这样做的.

  • 作为一个小小的附加说明,还有一些其他地方看起来像是向上转播.例如,`let o:obj = first [1; "1"]`.列表和数组文字有一些额外的逻辑来统一其成分的类型. (2认同)

nic*_*las 11

这是具有强大的类型推断机制的对手:通过使一切都明确,编译器更容易推断出什么是真的.

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

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

一个有用的构造,在它感觉简单多余的情况下,是添加演员

//The cast will be determined by the compiler, because of _
result :> _
Run Code Online (Sandbox Code Playgroud)

  • 举一个不能很好地工作的例子,想象两个分支返回两个不同的类,它们都实现接口`IA`和`IB`.编译器如何确定返回类型是"IA"还是"IB",还是"obj"?(这是一个大问题,因为它会显着影响代码的使用方式!) (4认同)
  • "结果:> _"的一个不错的替代方法是"向上结果". (3认同)
  • 不会像你说的那样对接口进行编程会导致代码散落在`:> _`中?由于返回接口的每个函数实际上都返回另一个实现该接口的对象.我认为它根本不会促进对接口的编程,因为类型被推测它会推断具体类型而不是接口. (2认同)
  • 你能引用一个例子,说明在表达式的分支中推断"最常见的"基类型实际上破坏了F#类型推断的其他特征吗?我的感觉总是由于哲学或复杂性(时间)而不是技术性而没有实现这样的功能. (2认同)