I'm trying to understand rules around type inference as I'd like to incorporate it into my own language, and in that spirit I've been playing around with F#'s type inference, and the following struck me as odd.
This compiles, and id
is 'a -> 'a
, which (if I am not mistaken), means that every invocation is using a "fresh" type.
let id x = x
let id1 = id 1
let id2 = id "two"
But when using an operator, it seems to be that the first invocation determines the signature for that function going forward.
Here, mul
is reported as being int -> int -> int
let mul x y = x * y
let mul1 = mul 1 2
let mul2 = mul 1.1 2.2 // fails here
If I reorder them, then mul
is float -> float -> float
:
let mul x y = x * y
let mul2 = mul 1.1 2.2
let mul1 = mul 1 2 // fails here
Could you explain (in preferably non-academical) terms what the rules are and perhaps how it works from the perspective of the type checking implementation? Does it walk over the functions to check their types every time they are referenced? Or is there some other approach?