The following works fine, as one would expect in a Curry style polymorphic type system where e.g. the identity function is actually an infinite family of functions, one for each type ‘a -> ‘a:
let f x = x
printfn "%A" (f 2)
printfn "%A" (f 3.4)
But when we try to do something a little more complex, it fails on the third line, “this expression was expected to have type int but here has type float”:
let f x = string x
printfn "%A" (f 2)
printfn "%A" (f 3.4)
(But commenting out the second line makes the third line work, as expected.)
A priori, I would have expected type inference to either behave Curry style where an unspecified type is effectively a generic, or else to lock down just one version of the function, but as far as I can see, it’s doing the former in the first case and the latter in the second case. I’m guessing there’s some logic behind the behavior that I’m just not picking up. What am I missing?
There is a non-standard thing going on here with “hat types”. Some F# functions like ‘string’ have types based on static optimization constraints or the existence of certain member functions. These can only be generalized in ‘inline’ functions, otherwise they take on a monomorphic type based on usage, and that’s what’s happening here.