FSharp和向接口的向上转换似乎是多余的

bra*_*ing 4 f# type-inference upcasting

我有以下使用反应式扩展的代码段:

    let value : 't = ...

    Observable.Create<'t>(fun observer ->
        let subject = new BehaviorSubject<'t>(value)
        let d0 = subject.Subscribe(observer)
        let d1 = observable.Subscribe(subject)
        new CompositeDisposable(d0, d1) :> IDisposable
    )
Run Code Online (Sandbox Code Playgroud)

这有效.但是,如果我将upcast丢弃到IDisposable,则代码无法编译,引用了模糊的重载.但是CompositeDisposable是一个IDisposable.为什么类型推理引擎无法解决此问题?注意我几乎一直在C#中使用这种模式从Observable.Create返回CompositeDisposable而不必进行upcast.

pad*_*pad 8

正如@kvb所说,函数不支持方差,因此接口和子类需要upcast.

这是一个演示子类行为的小例子:

type A() =
    member x.A = "A"

type B() =
    inherit A()
    member x.B = "B"

let f (g: _ -> A) = g()

let a = f (fun () -> A()) // works
let b = f (fun () -> B()) // fails
Run Code Online (Sandbox Code Playgroud)

如果函数f是由您编写的,则添加类型约束可能有所帮助:

// This works for interface as well
let f (g: _ -> #A) = g()

let a = f (fun () -> A()) // works
let b = f (fun () -> B()) // works
Run Code Online (Sandbox Code Playgroud)

否则,你必须按照你所描述的例子做一个litle upcast.

  • 我不认为这是正确的.对于特定类的子类,会发生相同的行为; 接口没有特定的东西.真正的问题是函数不支持方差,因此推断类型为"IObserver <t>" - >"CompositeDisposable"的参数不能被视为"IObserver <t> - > IDisposable". (3认同)
  • 这是类型推理引擎的基础还是可能在将来纠正的疏忽?显然有一个设计决定使接口显式,但我不明白为什么它需要这样. (2认同)