I'm having trouble understanding the letrec definition for HM system that is given on Wikipedia, here: https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system#Recursive_definitions
For me, the rule translates roughly to the following algorithm:
- infer types on everything in the
letrecdefinition part- assign temporary type variables to each defined identifier
- recursively process all definitions with temporary types
- in pairs, unify the results with the original temporary variables
- close (with
forall) the inferred types, add them to the basis (context) and infer types of the expression part with it
I'm having trouble with a program like this:
letrec
p = (+) --has type Uint -> Uint -> Uint
x = (letrec
test = p 5 5
in test)
in x
The behavior I'm observing is as follows:
- definition of
pgets temporary typea - definition of
xgets some temporary type too, but that's out of our scope now - in
x, definition oftestgets a temporary typet pgets the temporary typeafrom the scope, using the HM rule for a variable(f 5)gets processed by HM rule for application, resulting type isb(and the unification that (aunifies withUint -> b)((p 5) 5)gets processed by the same rule, resulting in more unifications and typec,anow in result unifies withUint -> Uint -> c- now, test gets closed to type
forall c.c - variable test of
in testgets the type instance (orforall c.c) with fresh variables, accrodingly to the HM rule for variable, resulting intest :: d(that is unified withtest::tright on) - resulting
xhas effectively typed(ort, depending on the mood of unification)
The problem: x should obviously have type Uint, but I see no way those two could ever unify to produce the type. There is a loss of information when the type of test gets closed and instance'd again that I'm not sure how to overcome or connect with substitutions/unifications.
Any idea how the algorithm should be corrected to produce x::Uint typing correctly? Or is this a property of HM system and it simply will not type such case (which I doubt)?
Note that this would be perfectly OK with standard let, but I didn't want to obfuscate the example with recursive definitions that can't be handled by let.
Thanks in advance