Search code examples
f#type-inferenceupcasting

FSharp and upcasting to Interfaces seems redundant


I have the following code snippet using the reactive extensions:

    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
    )

This works. However if I drop the upcast to IDisposable then the code fails to compile, citing ambiguous overloads. However CompositeDisposable is an IDisposable. Why is the type inference engine failing to resolve this? Note I use this pattern almost all the time in C# returning CompositeDisposable from Observable.Create without having to upcast.


Solution

  • As @kvb said, functions don't support variance so upcast is required for interfaces and subclasses.

    Here is a small example demonstrating the behavior with subclasses:

    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
    

    If function f is written by you, adding type constraints could help:

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

    Otherwise, you have to do a litle upcast as your example described.

    EDIT: Since F# 6.0, auto-upcasting of interfaces and subclasses is now supported by default.