一方、今回の一般的な型推論で必要とする new_typevarはそうはいかない。 なぜなら、普通の変数とは無関係な場所で型変数を作る必要があるから。 そこで、今回は、自然数で表される「カウンタ」を使い、 型変数の名前は、そのカウンタの値を含むようにした。 new_typevar は呼ばれるごとに、1ずつカウンタの値を増やすので、 複数の型変数の名前が衝突することはない。
ちなみに、このような場合、通常は、大域変数にカウンタの値を格納して更新していくのであ るが、この実験では、「徹頭徹尾純粋な関数型言語の機能だけを使う」という ポリシーなので、ここで述べる実装も、それに従っている。
let theta0 = ([] : tysubst)
(* new_typevar : int -> ty * int *)
let new_typevar n =
(TVar ("'a" ^ (string_of_int n)), n+1)
(* tinf2 : tyenv -> exp -> int -> tyenv * ty * tysubst * int *)
let rec tinf2 te e n =
match e with
| Var(s) ->
(try
let t1 = lookup s te in (te, t1, theta0, n)
with Failure(_) ->
let (tx,n1) = new_typevar n in
let te1 = ext te s tx in
(te1, tx, theta0, n1))
| IntLit(_) -> (te, TInt, theta0, n)
| BoolLit(_) -> ...
| Plus(e1,e2) ->
let (te1, t1, theta1, n1) = tinf2 te e1 n in
let (te2, t2, theta2, n2) = tinf2 te1 e2 n1 in
let t11 = subst_ty theta2 t1 in
let theta3 = unify [(t11,TInt); (t2,TInt)] in
let te3 = subst_tyenv theta3 te2 in
let theta4 = compose_subst theta3
(compose_subst theta2 theta1) in
(te3, TInt, theta4, n2)
| If(e1,e2,e3) -> ...
| Fun(x,e) ->
let (tx,n1) = new_typevar n in
let te1 = ext te x tx in
let (te2, t1, theta1, n2) = tinf2 te1 e n1 in
let t2 = subst_ty theta1 tx in
let te3 = remove te2 x in
(te3, TArrow(t2, t1), theta1, n2)
| App(e1,e2) ->
let (te1, t1, theta1, n1) = tinf2 te e1 n in
let (te2, t2, theta2, n2) = tinf2 te1 e2 n1 in
let (tx,n3) = new_typevar n2 in
let t11 = subst_ty theta2 t1 in
let theta3 = unify [(t11,TArrow(t2,tx))] in
let t3 = subst_ty theta3 tx in
let te3 = subst_tyenv theta3 te2 in
let theta4 = compose_subst theta3
(compose_subst theta2 theta1) in
(te3, t3, theta4, n3)
| _ -> failwith "unknown expression"
(* 以下は例題 *)
let tinf2top e = tinf2 [] e 0
let _ = tinf2top (If(BoolLit(true), IntLit(1), IntLit(100)))
let _ = tinf2top (If(Var("x"),Plus(Var("y"), IntLit(10)), IntLit(100)))
let _ = tinf2top (If(Var("x"),Plus(Var("y"), Var("z")), Var("z")))
let _ = tinf2top (Var("x"))
remove関数についての補足.
上記の Fun のケースにある (remove te2 x) は、
型環境 te2 から、変数xに対応する型宣言を除去して得られる型環境を意味する。
たとえば te2 = [("x13",TInt); ("x20",TBool)] のとき、
(remove te2 "x13") = [("x20",TBool)] である。
また、関数remove の型は tyenv -> string -> tyenvである。
亀山 幸義