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.