Let's rearrange the type constraints into a canonical order, duplicating all type constraints that contain a type variable on both sides of the equality.
ta = t16
ta = t29
ti = t26
tj = t24
tm = t3
tm = t12
tm = (tx → t21)
tx = t6
tx = t9
ty = t14
ty = t18
t1 = int
t2 = int
t3 = tm
t3 = (t1 → t4)
t4 = (t2 → t5)
t5 = t22
t6 = tx
t6 = int
t7 = bool
t7 = bool
t8 = int
t8 = t20
t9 = tx
t9 = int
t10 = int
t10 = int
t11 = int
t12 = tm
t12 = (t11 → t13)
t13 = (t14 → t15)
t14 = ty
t16 = ta
t16 = (t15 → t17)
t17 = (t18 → t19)
t18 = ty
t19 = t20
t20 = t19
t20 = t8
t21 = (ty → t20)
t22 = t5
t22 = t30
t23 = int
t23 = int
t24 = tj
t24 = int
t25 = int
t25 = int
t26 = ti
t26 = int
t27 = int
t28 = (tj → t27)
t29 = (ti → t28)
t30 = t22
A ground type is a type that contains no type variables. If one of our type constraints says that a type variable is equal to a ground type, then we know the value of that type variable. We might as well substitute its value for all occurrences. While we're at it, we will eliminate redundant type constraints.
ta = t16
ta = t29
ti = int
tj = int
tm = t3
tm = t12
tm = (tx → t21)
tx = int
ty = t14
ty = t18
t1 = int
t2 = int
t3 = tm
t3 = (int → t4)
t4 = (int → t5)
t5 = t22
t6 = tx
t6 = int
t7 = bool
t8 = int
t8 = int
t9 = tx
t9 = int
t10 = int
t11 = int
t12 = tm
t12 = (int → t13)
t13 = (t14 → t15)
t14 = ty
t16 = ta
t16 = (t15 → t17)
t17 = (t18 → t19)
t18 = ty
t19 = int
t20 = t19
t20 = int
t21 = (ty → int)
t22 = t5
t22 = t30
t23 = int
t24 = tj
t24 = int
t25 = int
t26 = ti
t26 = int
t27 = int
t28 = (tj → int)
t29 = (ti → t28)
t30 = t22
Those substitutions created some more constraints that have a ground type on the right hand side, so we substitute again.
ta = t16
ta = t29
ti = int
tj = int
tm = t3
tm = t12
tm = (tx → t21)
tx = int
ty = t14
ty = t18
t1 = int
t2 = int
t3 = tm
t3 = (int → t4)
t4 = (int → t5)
t5 = t22
t6 = int
t7 = bool
t8 = int
t9 = int
t10 = int
t11 = int
t12 = tm
t12 = (int → t13)
t13 = (t14 → t15)
t14 = ty
t16 = ta
t16 = (t15 → t17)
t17 = (t18 → int)
t18 = ty
t19 = int
t20 = int
t21 = (ty → int)
t22 = t5
t22 = t30
t23 = int
t24 = int
t25 = int
t26 = int
t27 = int
t28 = (int → int)
t29 = (ti → t28)
t30 = t22
And again:
ta = t16
* ta = (int → (int → int))
ti = int
tj = int
tm = t3
tm = t12
* tm = (int → t21)
tx = int
ty = t14
ty = t18
t1 = int
t2 = int
t3 = tm
t3 = (int → t4)
t4 = (int → t5)
t5 = t22
t6 = int
t7 = bool
t8 = int
t9 = int
t10 = int
t11 = int
t12 = tm
t12 = (int → t13)
t13 = (t14 → t15)
t14 = ty
* t16 = (int → (int → int))
t16 = (t15 → t17)
t17 = (t18 → int)
t18 = ty
t19 = int
t20 = int
t21 = (ty → int)
t22 = t5
t22 = t30
t23 = int
t24 = int
t25 = int
t26 = int
t27 = int
t28 = (int → int)
* t29 = (int → (int → int))
t30 = t22
That was the easy part. Now things get interesting. Let's focus on these constraints:
ta = t16
ta = (int → (int → int))
ti = int
tj = int
tm = t3
tm = t12
tm = (int → t21)
tx = int
ty = t14
ty = t18
t3 = (int → t4)
t4 = (int → t5)
t5 = t22
t12 = tm
t12 = (int → t13)
t13 = (t14 → t15)
t14 = ty
t16 = (int → (int → int))
t16 = (t15 → t17)
t17 = (t18 → int)
t18 = ty
t21 = (ty → int)
t22 = t5
t22 = t30
t28 = (int → int)
t29 = (int → (int → int))
t30 = t22
In particular:
ty = t14
ty = t18
t16 = (int → (int → int))
t16 = (t15 → t17)
t17 = (t18 → int)
t18 = ty
A matching process known as unification tells us that
ty = t14
ty = t18
* t15 = int
t16 = (int → (int → int))
t16 = (t15 → t17)
t17 = (t18 → int)
* t18 = int
t18 = ty
Adding these new constraints, and substituting equals for equals, we get
ta = (int → (int → int))
ti = int
tj = int
tm = t3
tm = t12
tm = (int → t21)
tx = int
* ty = int
t3 = (int → t4)
t4 = (int → t5)
t5 = t22
t12 = tm
t12 = (int → t13)
* t13 = (int → t15)
* t14 = int
t16 = (int → (int → int))
* t16 = (t15 → (int → int))
* t17 = (int → int)
t18 = int
* t21 = (int → int)
t22 = t5
t22 = t30
t28 = (int → int)
t29 = (int → (int → int))
t30 = t22
In particular:
tm = t3
tm = t12
tm = (int → t21)
t3 = (int → t4)
t4 = (int → t5)
t5 = t22
t12 = (int → t13)
t13 = (int → t15)
* t14 = int
t16 = (int → (int → int))
* t16 = (t15 → (int → int))
* t17 = (int → int)
t21 = (int → int)
t22 = t5
t22 = t30
t30 = t22
Substitution and unification yield
tm = t3
tm = t12
* tm = (int → (int → int))
* t3 = (int → (int → int))
* t4 = (int → int)
* t5 = int
* t12 = (int → (int → int))
* t13 = (int → int)
t14 = int
t15 = int
t16 = (int → (int → int))
* t16 = (int → (int → int))
t17 = (int → int)
t21 = (int → int)
* t22 = int
t22 = t30
* t30 = int
Hence we arrive at the following complete solution to the system of type constraints:
ta = (int → (int → int))
ti = int
tj = int
tm = (int → (int → int))
tx = int
ty = int
t1 = int
t2 = int
t3 = (int → (int → int))
t4 = (int → int)
t5 = int
t6 = int
t7 = bool
t8 = int
t9 = int
t10 = int
t11 = int
t12 = (int → (int → int))
t13 = (int → int)
t14 = int
t15 = int
t16 = (int → (int → int))
t16 = (int → (int → int))
t17 = (int → int)
t18 = int
t19 = int
t20 = int
t21 = (int → int)
t22 = t5
t22 = int
t23 = int
t24 = int
t25 = int
t26 = int
t27 = int
t28 = (int → int)
t29 = (int → (int → int))
t30 = int
The program is well-typed, and evaluates to an int.
Last updated 24 March 2008.