(* Hindley-Milner Type Inference Algorithm * * * Author: Adam Poswolsky * * This shows how to conduct type inference where * references + environments serve the role of references * in the standard Hindly-Milner style algorithms. * * Environments are Delphin functions mapping * parameters to types. * * We use a continuation-style approach to create * new parameters and continue computation with those new parameters. *) sig use "mini-ml.elf"; sig use "eval.elf"; sig use "value.elf"; sig use "tp.elf"; sig use "tpinf.elf"; params = , , ; (* expEnv maps "exp" variables to types *) type expEnv = -> ; (* tpEnv maps "tp" variables to other types. * This will be used in unification, as two types will * be considered equal if they are mapped to the same result, * i.e. if they point to the same data. *) type tpEnv = -> ; (* normalize (G, T) = T' such that T' is the type T where we follow all the pointers in G *) fun normalize : tpEnv -> -> = fn G => (case (G ) of => (* if it points to itself, then it is in normal form. *) | => normalize G ) | G => @ (normalize G ) @ (normalize G ) | G => @ (normalize G ) @ (normalize G ) | G => let val {} = {} normalize (G with => ) in end | G => ; (* * free : Our routine to deallocate references. * * i.e. free({} G) = G' * Where G' is G where all occurrences of are removed. * * If the reference cell "t" does not hold a concrete type, * then the expression we are typing is polymorphic in "t" * and we make it a "forall". *) fun freeG : ({} tpEnv) -> tpEnv = fn G => case {} normalize (G \t) of {} => | {} => ; fun freeGB : ({} tpEnv) -> tpEnv = fn G => case {} normalize (G \x) of {} => ; fun freeGtwice : ({} {} tpEnv) -> tpEnv = fn G => case {}{} normalize (G \t1 \t2) of {}{} => | {}{} => | {}{} => | {}{} => ; fun freeGtwiceB : ({} {} tpEnv) -> tpEnv = fn G => case {}{} normalize (G \t2 \x) of {}{} => | {}{} => ; fun freeGthrice : ({} {} {} tpEnv) -> tpEnv = fn G => case {}{}{} normalize (G \t1 \x \t2) of {}{}{} => | {}{}{} => | {}{}{} => | {}{}{} => ; (* Unification.. * * unifyTypes G T1 T2 = G' * G' is an an update to the mapping in G to make T1 == T2 *) fun unifyTypes : tpEnv -> -> -> tpEnv = fn G T1 T2 => unifyTypesN G (normalize G T1) (normalize G T2) and unifyTypesN : tpEnv -> -> -> tpEnv = fn G => G | G => (fn => | => G ) | G => (fn => | => G ) | G => let val G2 = unifyTypes G val G3 = unifyTypes G2 in G3 end | G => let val G2 = unifyTypes G val G3 = unifyTypes G2 in G3 end | G => (case ({} let val Gnew = (G with => ) in unifyTypes Gnew end) of Gextended => freeG Gextended (* gets rid of occurrence of t *) ) | G => G; (* checkTypeSchema G K = G' * This function checks that the schema (Tschema) is compatible with argument (Targ) * and that the result of the application is Tresult. * * where K is a continuation *) fun checkTypeSchema : tpEnv -> -> -> -> (tpEnv -> tpEnv) -> tpEnv = fn G K => checkTypeSchemaN G (normalize G ) K and checkTypeSchemaN : tpEnv -> -> -> -> (tpEnv -> tpEnv) -> tpEnv = fn G K => let val G2 = unifyTypes G val G3 = unifyTypes G2 in K G3 end | G K => (case ({} let val G' = (G with => ) in checkTypeSchema G' K end) of Gextended => freeG Gextended); (* checkType (W, G, E, T, K) = G' * * This is the MAIN function where * * W : map of exp variables * G : map of tp variables * E : exp we are typing * T : desired type of E * * K is the continuation returning a G' *) fun checkType : expEnv -> tpEnv -> -> -> (tpEnv -> tpEnv) -> tpEnv = fn W G K => K (unifyTypes G (W ) ) | W G K => K (unifyTypes G ) | W G K => checkType W G (fn G2 => K (unifyTypes G2 )) | W G K => checkType W G (fn G2 => checkType W G2 (fn G3 => case {} checkType (W with => ) G3 K of Gextended => freeGB Gextended)) | W G K => (case {}{} let val G' = G with => | => in checkType W G' (fn G2 => checkType W G2 (fn G3 => K (unifyTypes G3 ))) end of Gextended => freeGtwice Gextended) | W G K => (case {}{} let val G' = G with => | => in checkType W G' (fn G2 => K (unifyTypes G2 )) end of Gextended => freeGtwice Gextended) | W G K => (case {}{} let val G' = G with => | => in checkType W G' (fn G2 => K (unifyTypes G2 )) end of Gextended => freeGtwice Gextended) | W G K => (case {}{}{} let val W' = W with => val G' = G with => | => in checkType W' G' (fn G2 => K (unifyTypes G2 )) end of Gextended => freeGthrice Gextended) | W G K => (case {}{} let val G' = G with => | => in checkType W G' (fn G2 => checkType W G2 (fn G3 => checkTypeSchema G3 K)) end of Gextended => freeGtwice Gextended) | W G K => let fun getType : ({} tpEnv) -> = fn GG => case {} normalize (GG \t) of {} => | {} => val Gnew = {} checkType W G (fn G' => G') val = getType Gnew val Gnew' = freeG Gnew in case {}{} let val W' = (W with => ) val G' = (Gnew' with => ) in checkType W' G' (fn G3 => K (unifyTypes G3 )) end of Gextended => freeGtwiceB Gextended end | W G K => let fun getType : ({} tpEnv) -> = fn GG => case {} normalize (GG \t) of {} => | {} => val Gnew = {} checkType W G (fn G' => G') val = getType Gnew val Gnew' = freeG Gnew in case {} let val G' = Gnew' with => in checkType W G' (fn G3 => K (unifyTypes G3 )) end of Gextended => freeG Gextended end | W G K => (case {}{} let val W' = (W with => ) val G' = (G with => ) in checkType W' G' (fn G2 => K (unifyTypes G2 )) end of Gextended => freeGtwiceB Gextended); val inferType : -> = fn => let val G = {} checkType (fn .) (fn => ) (fn G' => G') fun getType : ({} tpEnv) -> = fn G => case {} normalize (G \t) of {} => | {} => in getType G end; val example3' = inferType ; val example4' = inferType ;