back numbers

8.04.2008

ET Seminar 2008 (3)

等価変換理論集中セミナー2008
表現したい状態の集合を論理式で圧縮することで、(状態数増加による開発の)困難を克服するという流れでPrologの狙いを説明して来た。仕様として想定される状態(今風に云えばテストケースに近いかもしれない)から実装を導くという方針もPrologでは重視されている。しかし実際にPrologで規模を伴なうプログラムの開発を行うと、思ったほど楽ではない事が体験出来る。ではPrologの問題点は何なのか。

まず特徴的なPrologの表現形式だが、これはS式と思っても構わない。
human(socrates).
≡ (human socrates)

die(X) :- human(X).
≡ (:- (die X) (human X))
また連続するアトムに対する結合性などは op/3 で演算子ごとに定義出来る。

% current_of(400, yfx, '/').
:- op(1100, yfx, '<').
X / _ / X < 'hello'.
≡ (< (/ (/ X _) X) 'hello')

プログラムがこのように単純な構造を持つため、ヘッドに適当なアトム列を想定する事で、表記については柔軟な対応が可能だと分かる。しかしマクロやDSLを実現するにはこれだけでは片手落ちなのである。

マクロによって表現されたプログラムは、当然そのプログラムが実行される前に実行可能な形式、つまりマクロが展開された形になっている必要がある。しかしPrologには述語を制御する仕様は存在してもランタイムを制御する仕様は(部分的にしか)無いため、マクロの実行というセマンティクスをプログラムに埋め込んでやる必要が出てくる。典型的なDOループマクロを見てみる。

:- op(1100, xfy, do).
(foreach(Elem, List) do Template) :- do_loop(List, iter(Elem, Template)).
do_loop([], _Template).
do_loop([Value|Rest], Template) :-
copy_term(Template, Copy),
Copy = iter(Value, Goals),
call(Goals),
do_loop(Rest, Template).

?- foreach(X, [1,2,3]) do write(X), nl.
call foreach(G1, [1,2,3]) do write(G1), nl
call do_loop([1|[2,3]], iter(G1, (write(G1), nl)))
call copy_term(iter(G1, (write(G1), nl)), G2)
exit copy_term(iter(G1, (write(G1), nl)), iter(G2, (write(G2), nl)))
call iter(G2, (write(G2), nl)) = iter(1, G4)
exit iter(1, (write(1), nl)) = iter(1, (write(1), nl))
call call((write(1), nl))
1
exit call((write(1), nl))
call do_loop([2,3], iter(G1, (write(G1), nl)))
call copy_term(iter(G1, (write(G1), nl)), G5)
..

展開したマクロと元のテンプレートで変数が被らないよう(健全性)にする copy_temr/2や、変数束縛(マクロ展開)を指示するための =/2 による単一化、そしてクエリ(展開されたマクロ)を実行する call/1 といった具合で、完全に手作業なのである。これを柔軟性と呼ぶのは些か憚られるだろう。

二階の述語論理も同様の手法で実現される。

map_list(_Fn, [], []).
map_list(Fn, [X|Xs], [Y|Ys]) :-
G =.. [Fn, X, Y],
call(G),
map(Fn, Xs, Ys).
inc(X, Y) :- Y is X + 1.

?- map(inc, [1,2,3], L).
L = [2,3,4].

高階論理の実現に言語拡張が必要無い分、楽だと云えなくもない。型の判別には functor/3 や atom/1 や number/1 や compound/1 や var/1 などが用意されているから、これらを利用することで高階論理の型チェックも出来ない事はない。しかし全てはランタイムの振舞いとしてプログラミングされるだけであり、よく整備してもライブラリとしてパッケージ化するのが限度となる。ちなみに高度な例としてはDefinite Clause Grammarがある。

ここで(当時の)論理型プログラミングが持つ根本的な問題点が浮かびあがってくる。状態の集合を論理的に述べる事は出来ても、高階論理を書く事が出来ても、それらは全て実行時の結果としてしか得ることが出来ない。抽象化というプロセスについては何の支援も行なってくれないのである。例えばタイピングをどの時点でどのように与えるかという問題にしても、他の言語は動的か静的、また或いはリテラルベースか即値ベースかなどの選択肢があるのに対し、Prologが出来ることは精々、

speak(cat, 'meow').
speak(dog, 'woof').
という程度なのである。これを簡単と捉えるのか、ただ具体的なレコードと見るのか?

斯くなる特徴(有利・不利は状況次第だろうが)をPrologはプログラマに強制させるという点が、実際の開発では大きな障害となる。だがPrologの問題点は言語仕様だけでなく、その実行セマティクスにもある。
(続く)

0 件のコメント:

tags

Profile

Taito, Tokyo, Japan
明けども明けども次の埒
hiro.kosh@gmail.com