Say, I have
member this.Test (x: 'a) = printfn "generic"
1
member this.Test (x: Object) = printfn "non generic"
2
If I call it in C#
var a = test.Test(3); // calls generic version
var b = test.Test((object)3); // calls non generic version
var c = test.Test<object>(3); // calls generic version
However, in F#
let d = test.Test(3); // calls non generic version
let e = test.Test<int>(3); // calls generic version
So I have to add type annotation so as to get the correct overloaded method. Is this true? If so, then why F# doesn’t automatically resolve correctly given that the argument type is already inferred? (what is the order of F#’s overload resolution anyway? always favor Object than its inherited classes?)
It is a bit dangerous if a method has both overloads, one of them takes argument as Object type and the other one is generic and both return the same type. (like in this example, or Assert.AreEqual in unit testing), as then it is very much likely we get the wrong overloading without even notice (won’t be any compiler error). Wouldn’t it be a problem?
Update:
Could someone explain
-
Why F# resolves
Assert.AreEqual(3, 5)asAssert.AreEqual(Object a, Object b)but notAssert.AreEqual<T>(T a, T b) -
But F# resolves
Array.BinarySearch([|2;3|], 2)asBinarySearch<T>(T[]array, T value)but notBinarySearch(Array array, Object value)
I don’t think it’s true. Method overloading makes type inference much more difficult. F# has reasonable trade-offs to make method overloading usable and type inference as powerful as it should be.
When you pass a value to a function/method, F# compiler automatically upcasts it to an appropriate type. This is handy in many situations but also confusing sometimes.
In your example,
3is upcasted toobjtype. Both methods are applicable but the simpler (non-generic) method is chosen.Section 14.4 Method Application Resolution in the spec specifies overloading rules quite clearly:
I think it’s users’ responsibility to create unambiguous overloaded methods. You can always look at inferred types to see whether you’re doing them correctly. For example, a modified version of yours without ambiguity:
UPDATE:
It comes down a core concept, covariance. F# doesn’t support covariance on arrays, lists, functions, etc. It’s generally a good thing to ensure type safety (see this example).
So it’s easy to explain why
Array.BinarySearch([|2;3|], 2)is resolved toBinarySearch<T>(T[] array, T value). Here is another example on function arguments whereis resolved to
but not to