Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 960
bradgonesurfing Avatar asked Feb 19 '23 02:02

bradgonesurfing


1 Answers

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.

like image 80
pad Avatar answered Feb 20 '23 16:02

pad