Linked Questions

Popular Questions

What are the rules for automatic upcasting of functions?

Asked by At

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.

Related Questions