ゆにふぃやー
と、いうわけで、なんか、100行ぐらいのコードを書いては捨て、書いては捨てを3回くらい繰り返してるうちに、いつのまにかネタストックはどうでもよくなってしまったので、ILogScriptについて考える。
ILogScriptは僕の中でネタ切れした時のクッションの役割を担っているのだ!!(意味不明)
http://d.hatena.ne.jp/w_o/20050728#p4
うーん。明日書くって書いたのがいつのはなしだ…実に、構想一時間、放置三カ月のネタ。
どうしたらいいかわからなくて放置してた、ILogScriptの型チェックライブラリなんだけど、Prologもどき、というか、unificationみたいなのをうまくやればいいんじゃないか、っていう話。
まず、
// add_float:: (float,float) -> float // add_int:: (int,int) -> int // float_to_int:: (float) -> int function fun(x,y,z) = return add_int( x, float_to_int( add_float(y,z) ) );
こんな関数があったとしよう。
これの型は、
fun:: (int,float,float)->int
になるはず。
それで、だ。型推論したいわけなんだけど、なんと、Prolog(のようなもの)は型推論エンジンに使えるのである。(多分)
上の関数funの定義を
add_int( [int,int] ,int). add_float( [float,float], float ). float_to_int( [float], int ). fun( [X,Y,Z], Ret ) :- add_float( [Y,Z] , Tmp1 ), float_to_int( [Tmp1], Tmp2 ), add_int( [X,Tmp2], Ret ).
こういうふうなPrologに変換するとしよう。上の関数定義プログラムは構文木に変換してしまえば、下のPrologプログラムに変換するのはそんなに難しくない。スタックマシンのコードを生成するのと同じぐらいの労力でできる。多分。
すると、あとは、これをPrologインタプリタに突っこんでやるだけで、
?- fun( [X,Y,Z], Ret ). X = int Y = float Z = float Ret = int Yes
正しく、型推論された結果が出てくるのだ。
function swap(x,y) = let v = *x in *x = *y *y = v
こーんな関数を考えたとしても、
deref( pointer_to(T), T ). swap( [X,Y], Ret ) :- ref( Y, Tmp ), V = Tmp, deref( X ) = deref( Y ), deref( Y, Tmp1 ), Tmp1 = V.
こういうPrologを考えて(これも構文木から簡単に生成できるはず)実行したとすると、
?- swap( [X,Y], _ ). X = pointer_to(_G227) Y = pointer_to(_G227) Yes
XとYは汎用な型へのポインタ型っていうのを推論できるのだ。
と、いうわけで、コンパイラから呼べる、Prologみたいな単一化をしてくれる「なんか」があれば、型推論ライブラリとして使えそうだ。と、思ったのが、三カ月前。
だがしかし、その「なんか」がどういうインターフェースにしたらいいかわからんので、一歩も進まないままなのであった。
とりあえず、考えたのが、
var unifier = gen_unifier(); ... unifier.run();
こんなふうに実行できるunifierオブジェクトを作れるようにすると考えて、
function unmatch() { error("type unmatch in " + lineno + ".\n"); } unifier.add_call( ..., unmatch ); unifier.add_unify( var1, var2, unmatch );
そのunifierにはなんか節のようなものを追加していける。ここで、この節が失敗したときのハンドラを指定できるようにしとく。
function type_determine(node,val) { node.type = val; // このノードの型を決定 } var type_var = unifier.make_var( "name", type_determine, obj );
こんな感じで、unifier内の変数を作れる。ここで、この変数の値が決定したときのハンドラと、そのハンドラに渡す引数を指定できるようにしとく。
と、いったところ。
で、推論処理の実体をunifer.runの中に閉じこめておけば中身は全部Cで書けるから遅くはならないはず。メモリ割り当てがちょっと多いかもしれん。そこらへんはやってみないとわかんない。
あと、タグ付けした引数とかが無いと、レコード型の推論がアレ。タグ引数はPrologにもあるのかしら。Prologはあんまりわからんので、もうちょっとそこらへんを調べておく、こと。
あと、根本的な問題として、単一化のアルゴリズムがわかんない。そこらへんも調べること。