In my code I have a scenario where I'd like F# to automatically upcast values of one function type (e.g. IBase -> unit
) to another, explicitly specified function type (IDerived -> unit
). While this seems to be generally supported, I found cases where it fails for no obvious reason.
In the code below, case1
to case8
seem equivalent to me: In each case, there's an expression of type IBase -> unit
on the right hand side that is bound to a name of type IDerived -> unit
. So why are cases 4, 5 and 7 not allowed?
type IBase = interface end
type IDerived = inherit IBase
let foo (x: IBase) = ()
let bar = fun (x: IBase) -> ()
type T = T with static member (!!) (_: T) = foo
let baz = !!T
let case1: IDerived -> unit = foo // OK
let case2: IDerived -> unit = bar // OK
let case3: IDerived -> unit = baz // OK
let case4: IDerived -> unit = !!T // Expecting 'IDerived -> unit' but given 'IBase -> unit'
let case5: IDerived -> unit = upcast !!T // Type 'IBase -> unit' is not compatible with type 'IDerived -> unit'
let case6: IDerived -> unit = let z = !!T in z // OK
let case7: IDerived -> unit = fun (x: IBase) -> () // Expected x to have type 'IDerived' but here has type 'IBase'
let case8: IDerived -> unit = let z = fun (x: IBase) -> () in z // OK
EDIT: To clarify, I'm mostly wondering why the 8 cases are not treated equally by the compiler. For example, I'd expect the binding let z...
in case8
to be superfluous, yet it makes a difference (compared to case7
). Why?
EDIT: Here's some code to demonstrate what I'm trying to achieve: https://dotnetfiddle.net/AlpdpO It also contains a solution/workaround, so I'm really asking more for technical details/reasons rather than alternative approaches. I thought about raising an issue on GitHub already, but feel like the issue is most likely just a misunderstanding on my side.