Code to help with CS152 Assignment: ML Type Inference
To print type schemes, we use:
<<*>>=
fun separate (zero, sep) = (* useful for printing *)
let fun s [] = zero
| s [x] = x
| s (h::t) = h ^ sep ^ s t
in s
end
local
(* precedences *)
val CONp = 3
val STARp = 2
val ARROWp = 1
val NONEp = 0
fun parens s = "(" ^ s ^ ")"
fun bracket(s, context, prec : int) = if prec <= context then parens s else s
fun p(context, CONty ("tuple", l)) = bracket(ptuple l, context, STARp)
| p(context, CONty (n, [])) = n
| p(context, CONty (n, [t])) = p(CONp, t) ^ " " ^ n
| p(context, CONty (n, l)) = "(" ^ separate("", ", ") (map ?? l) ^ ") " ^ n
| p(context, VARty v) = "'" ^ v
| p(context, ARROWty (arg, ret)) =
bracket(p(ARROWp, arg) ^ " -> " ^ p(ARROWp, ret), context, ARROWp)
and ptuple l = separate("unit", " * ") (map (fn t => p(STARp, t)) l)
and ?? ty = p(NONEp, ty)
in
val ?? = ??
val ptuple = ptuple
end
@
@
Here
are a bunch of functions on types, including finding free type
variables and putting types into canonical form by making the
variables [['a]], [['b]], etc.
<<*>>=
fun member(x, []) = false
| member(x, h::t) = x = h orelse member(x, t)
fun insert(x, l) = if member(x, l) then l else x::l
fun freevars t =
let fun f(VARty v, l) = insert(v, l)
| f(CONty(_, args), l) = foldr f l args
| f(ARROWty(arg, ret), l) = f(ret, f(arg, l))
in f(t, [])
end
local
val n = ref 1
in
fun freshtyvar _ = VARty ("t" ^ makestring (!n) before (n := !n + 1))
end
@
Note that to put things in canonical form, I first make all the free
type variables fresh type variables, so that when I start making type
variables into [["a"]] and so on, I don't collide with variables that
are already there.
<<*>>=
fun canonical_tyvar n =
VARty (if n < 26 then str(chr (ord #"a" + n)) else "v" ^ makestring (n - 25))
fun canonicalize ty =
let exception Impossible
fun s ([], to_fresh, to_canon, count) = to_canon o to_fresh
| s (v::rest, to_fresh, to_canon, count) =
(case freshtyvar()
of (ty' as (VARty v')) =>
s(rest, v |--> ty' o to_fresh,
v' |--> canonical_tyvar count o to_canon, count+1)
| _ => raise Impossible)
in s(freevars ty, fn x => x, fn x => x, 0) ty
end
fun canonicalize_pair(t1, t2) =
let exception Impossible
in case canonicalize (CONty("tuple", [t1, t2]))
of CONty("tuple", [t1, t2]) => (t1, t2)
| _ => raise Impossible
end
@
If you don't want to use
functions to represent substitution, here's an alternative suggestion:
<>=
infix 7 |-->
infix 3 o'
datatype subst = op |--> of string * ty
| op o' of subst * subst
| idsubst
fun apply (sigma o' sigma') ty = apply sigma (apply sigma' ty)
| apply idsubst ty = ty
| apply (v |--> t) ty = (* you write this part *)
@
I haven't tested this code, but if you use it, you'll be able to print
substitutions by defining a suitable function. You'll use [[o']]
instead of [[o]] to compose them, of course...