DKLIC 処理系における分散論理変数の資源管理 DKLIC resource management for distributed logic variables

早稲田大学大学院理工学研究科情報科学専攻高木祐介 TAKAGI Yusuke

2001-12-04

1 背景

複数のプロセスが協調処理を行う分散アプリケーションの実装は、逐次アプリケーションの実装に比べて非常に難しい。既に普及している手続き型言語の分散拡張はネットワーク透過性が不十分であるため、逐次アプリケーションを単純に分散拡張することができない。

既存の分散プログラミング機構として、次のようなものがある。

1. ソケットは遠隔プロセスと通信するための機構である。サービスを要求するクライアントとサービスを提供するサーバから成る、クライアント/ サーバモデルに基づいている。ソケットは BSD に由来するが、 C 言語のソケットインタフェイスは System V 系や Windows へも移植された。 Java ではソケットインタフェイスが言語レベルで定義されている。

2. RPC (Remote Procedure Call) や Java RMI は、手続き・関数を呼び出す形式で遠隔のサービスを受ける機構である。ソケットへの入力が呼び出しの引数として、ソケットからの出力が関数の値として、透明化される。透明化が不完全であるのは、オブジェクトに対して参照・逆参照の操作を明示するせいである。

3. CORBA (ORB) や Jini は分散オブジェクトを管理し、遠隔のサービス（オブジェクト）に対する要求を仲介する機構である。 RPC との本質的な差は、分散オブジェクトの登録・検索 (Name Service) による capability の取得にある。

4. モバイルエージェントは、実行状態を含むプロセスの移動によって通信を透明化する機構である。 RPC や ORB との差は、通信されるメッセージが単なるデータではなく自律的に計算・移動を行うプロセスである点にある。

これら既存の分散プログラミング機構は C や Java などの手続き型言語に基づいており、参照のネットワーク透過性が不十分である。

2 分散 KL1 言語の挑戦

KL1 言語は最も単純な並行論理型言語の一つである。述語呼び出しによって並行プロセスを動的生成し、論理変数を通信路として非同期プロセス間通信を行う。論理変数を含むメッセージを通信路に流すことによって、論理ネットワークを動的構成することができる。

ユーザや遠隔プロセスもまた、並行プロセスの一種であると考えられる。従って、並行プロセスと非同期プロセス間通信を用いる KL1 言語のプログラミングスタイルは、分散プロセスによる協調処理の記述に適していると言える。

論理変数は通信路である。例えば、次の append 述語はリスト X と Y を連結したリスト Z を返す。

append(X, Y, Z) :- X = [] |
  Z = Y.
append(X, Y, Z) :- X = [A | X1] |
  Z = [A | Z1],
  append(X1, Y, Z1).

クライアントがこの述語を呼び出すと append プロセスが生成される。関数型言語では X の中身が完全に確定した時に Z を全て確定する。しかし、このプロセスは X の中身が不確定ならば中断し、 X の中身が徐々に確定するにつれて Z の中身を前から確定していく。

すなわち、 append プロセスは一種のサーバである。 X と Y はクライアントからサーバへの通信路であり、 Z はサーバからクライアントへの通信路である。また、 X, Y, Z の要素は通信路に流れるメッセージである。

X, Y, Z に流れるメッセージが整数や記号アトムなどの単純なものであれば、 Unix 入出力ストリームや RPC で簡単に実装することが可能である。

しかし、 KL1 言語では論理変数を含む複雑なメッセージを通信路に流すことによって、通信路を動的生成することが可能である。複雑な通信プロトコルの宣言的記述を許すために、分散 KL1 言語処理系は論理変数の輸出入と分散単一化を実現しなければならない。

3 DKLIC 処理系の目標

DKLIC 処理系の基本サーバ dklic は、以下のような宣言型広域分散プログラミングを可能にする。

次の chat サーバは、複数のクライアントの発言をそれぞれブロードキャストする。

main :- true |
  dklic:bind(line_io, R),
  main(R).
main(R) :- R = normal(ServerSocket) |
  generic:new(merge, Clients, Broadcast),

                                              chat(ServerSocket, Clients, Broadcast).
                                            chat(Chat, Clients, Broadcast) :-
                                                Chat = [accept(R) | Chat1] |
                                              chat(R, Chat1, Clients, Broadcast).
                                            alternatively.
                                            chat(Chat, Clients, Broadcast) :-
                                                Broadcast = [Line | Broadcast1] |
                                              chat(Chat, Clients, Broadcast1).
                                            chat(R, Chat, Clients, Broadcast) :-
                                                R = normal(I, O) |
                                              Clients = {I, Clients1},
                                              O = Broadcast,
                                              chat(Chat, Clients1, Broadcast).

クライアントとの通信路 Client, Broadcast は、発言が時系列順に並ぶストリームとして見える。

クライアント側は Client = [] によって発言ストリームを閉じ、 Broadcast への参照を捨てるだけで良い。すると Broadcast への参照がなくなったことを dklic が検出し、後始末をする。これは分散論理変数の輸出入表においてエントリの削除を行うことによって、初めて可能となる。

chat サーバは人間同士が単純な文字列をやりとりするだけであるが、ごく簡単な改造によって、機械同士が複雑なプロトコルで通信し合うサーバを実現できる。

4 DKLICP 言語

1. 正則文法 lex : String → T oken^{∗}. KL1 言語の字句解析と同じ

2. 文脈自由文法 parse : T oken^{∗} → T erm^{∗}. KL1 言語の構文解析と同じ

3. 文脈依存文法 unmarshal =_{def} marshal^{−1} : (String →)T erm^{∗}. 分散論理変数・輸出入表が定義する 4. 意味論. （ユーザ定義）サーバによって定義さ

れる

lex, parse を KL1 言語と共有することによって、 DKLIC 処理系は KL1 言語の既存パーザをそのまま利用することが可能である。

1. パケットの欠損や追い越しがないネットワークを前提とする。

2. 論理変数を送信する時は、下線に続けてその論理変数の ID をネットワークに流す。論理変数以外の項は、その項の KL1 プログラムにおける文字列表現をネットワークに流す。すなわち、 KL1 項から文字列への変換 marshal : T erm → String において、

marshal(t) = ‘t’ if t ∈ Atomic marshal(t) = ‘ ’ + marshal(id)

if t ∈ V ar ∧ (id, t) ∈ table marshal(t) = ‘NEW ’ + marshal(id)

if t ∈ V ar ∧ (id, t) ∉ table where add(table, ns, t) = (id, table_{1}, ns_{1})

marshal(t) = ‘[’ + marshal(x) + ‘|’ + marshal(y) + ‘]’ if t = [x|y] ∈ Cons

marshal(t) = marshal(f) + ‘(’ + marshal(x_{1}) + ⋯ + marshal(x_{n}) + ‘)’ if t = f(x_{1}, …, x_{n}) ∈ F unctor

5 dklic の設計

5.1 Layers

1. 分散論理変数 distributed

2. 輸出入表 variable table ( ⇔ ID manager )

3. 変数入出力ソケット io

5.2 分散論理変数 distributed

(id, var) ∈ Dist =_{def} ID × V ar

d(ID, X, Report) :- wait(X) |
Report = [bound(ID, X)].
alternatively.
d(ID, X, Report) :- unified(X) |
Report = [unified(ID, X) | Report1],
d(ID, X, Report1).
d(ID, X, Report) :- garbage(X) |
Report = [collected(ID)].

概念的には上のようなクラス distributed を作りたいのだが、 GC された distributed は自分の後始末をできないので、親である輸出入表が後始末する必要がある。

1. コンストラクタ + ID -Address ? Report

2. イベント unify ?Any ローカルホスト内で distributed が単一化されたことを示すイベントである。ローカルホスト内の制約記憶（単一化の履歴）を constraints とすると、 unify : Dist × Constraints × T erm →

Constraints × {bound/2, unified/2} unify(id, var, constraints, term) =

(constraints ∪ {var = term},

message(id, term))

where

message = bound if term ∈ Bound message = unified if term ∉ Bound

distributed を Generator Object として実装することによって、 distributed 同士の単一化を検出できないだろうか?

3. イベント gc (-Address)

ローカルホスト内で GC が行われ、ユーザプロセスからこのオブジェクトへの参照が生存しているので、このオブジェクトを新領域へコピーする必要があることを示すイベントである。輸出入表からこのオブジェクトへの参照は「生存していない」ので、それ以外の参照が生存していない場合、このオブジェクトは旧領域へ取り残され、廃品として回収される。 gc : ID → moved gc(id) = moved(id)

5.3 ID manager (Name Server)

Name Server は概念的に様々なレベルで現れる。例えば、

1. DNS はホスト名を IP アドレスに対応付ける。

2. ソケットはポート番号をサーバに対応付ける。

3. ID manager は ID を分散論理変数に対応付ける。

4. java.rmi.Naming (rmiregistry) はインタフェイスをサーバオブジェクトに対応付ける。

5. ORB はサービスを分散オブジェクトに対応付ける。

DNS, ORB は実体に、異なる名前空間における別名を付ける。ユーザは実体の本名を知る必要がなく、別名によって実体を特定することができる。

ソケット, ID manager, Java RMI は実体に広域名を付けることによって、遠隔ユーザが実体にアクセスすることを可能にする。

Name Server に必須の機能ではないが、 (DNS,) Java RMI, ORB には検索機能がある。

NS : ID → {free, used}

get : NS → ID × NS
release : NS × ID → NS

get(ns) = (id, ns \ {(id, free)} ∪ {(id, used)}) release(ns, id) = ns \ {(id, used)} ∪ {(id, free)}

NS の機能は輸出入表（配列）に含めることが可能であるが、新しい ID を O(1) で取得するために未使用 ID をリストで管理する。リストの扱いは C 言語より KL1 言語の方が得意としているので、 NS は KL1 言語によって記述し、輸出入表 (generic object written in C) とは別プロセスとする。

ns(NS, Free) :- NS = [get(ID) | NS1] |
  Free = [ID | Free1],
  ns(NS1, Free1).
ns(NS, Free) :- NS = [release(ID) | NS1] |
  Free1 = [ID | Free],
  ns(NS1, Free1).

概念的には上のような NS を作るが、 ID 空間が実際には有限であることを考慮する必要がある。

例えば、未使用 ID がない時に get されたら輸出入表を広げる。輸出入表が縮んだら未使用 ID を再計算する。

輸出入表（配列）のインデクスを表す integer を ID として使用する。

5.4 輸出入表 variable table

{(id, var)} ∈ T able =_{def} 2^{Dist}

∀i, j; (id_{i}, var_{i}), (id_{j}, var_{j}) ∈ table ⇒

(id_{i} = id_{j} ⇔ var_{i} = var_{j})  (1)

論理変数同士の同一性を判定することが可能であることを前提とする。同一の論理変数が輸出入表において異なるエントリを持つことはない (1)。

State = T able × NS
∀id, ns, table;
(ns(id) = used ⇔ (id, var) ∈ table) ∧
(ns(id) = free ⇔ (id, var) ∉ table) (2)

variable table は NS と連動する。 NS は variable table にカプセル化され、他のプロセスからは見えない (2)。

クラス variable table

1. コンストラクタ +Length ? Report 2. イベント gc (-Length)

ローカルホスト内で GC が行われていることを示すイベント。 variable table は自分自身を新領域へコピーするが、エントリ distributed をコピーしない。 GC 終了直後に廃品として回収された distributed の後始末をする。

3. io からのメッセージ add - ID ?Var (-Length) io が variable table に登録されていない論理変数を輸出したい時、または io が遠隔ホストから ‘NEW ID’ を受け取った時に発するメッセージである。 add : State × V ar → ID × State add(table, ns, var) =

(id, table ∪ {id}, ns_{1})

where get(ns) = (id, ns_{1})

4. io からのメッセージ remove + ID io が遠隔ホストから ‘ ID= ’ を受け取った時に発するメッセージである。 remove : State × ID → State remove(table, ns, id) =

(table \ {(id, var)}, release(ns, id))

5. io からのメッセージ getID - ID ?Var io が variable table に登録済みの分散論理変数を遠隔ホストに送信したい時、ローカルホスト内の論理変数に対応する ID を要求するメッセージである。 getID : State × V ar → ID getID(table, ns, var) = id

where (id, var) ∈ table

6. io からのメッセージ get + ID ?Var io が遠隔ホストから ‘ ID’ を受け取った時に、 ID に対応するローカルホスト内の論理変数を要求するメッセージである。 get : State × ID → V ar × State get(table, ns, id) = (var, table, ns)

where (id, var) ∈ table

7. io からのメッセージ set + ID +Bound io が遠隔ホストから ‘ ID=Bound’ を受け取った時に発するメッセージである。 set : State × Constraints × ID × Bound →

State × Constraints set(table, ns, constraints, id, bound) = (table \ {(id, var)}, release(ns, id), constraints ∪ {var = bound})

8. io からのメッセージ unify + ID +ID1 io が遠隔ホストから ‘ ID= ID1’ を受け取った時に発するメッセージである。 unify : State × ID × ID → State unify(table, ns, id, id_{1}) =

(table \ {(id, var), (id_{1}, var_{1})} ∪ {(id_{1}, var)}, release(ns, id))

9. distributed からのメッセージ bound + ID +Bound distributed が具体化された時に発するメッセージである。 bound : State × ID × Bound → State bound(table, ns, id, bound) = remove(table, ns, id)

10. distributed からのメッセージ unified + ID +Var distributed が他の distributed と単一化された時に発するメッセージである。 unified : State × ID × V ar → State unified(table, ns, id, var) = (table, ns) if id < getID(var) = remove(table, ns, id) if id > getID(var)

11. distributed からのメッセージ moved + ID +Address distributed が GC 中に新領域へコピーされた時に発するメッセージである。

                                        vt(VT, T, NS) :- VT = [add(ID,X) | VT1] |
                                          NS = [get(ID) | NS1],
                                          T1 = [(ID,X) | T],
                                          vt(VT1, T1, NS1).

vt(VT, T, NS) :- VT = [remove(ID) | VT1] |
T1 = T - [(ID,_)],
NS = [release(ID) | NS1],
vt(VT1, T1, NS1).
vt(VT, T, NS) :- VT = [get(ID,X) | VT1] |
member((ID,X), T),
vt(VT1, T, NS).
vt(VT, T, NS) :- VT = [set(ID,Bound) | VT1] |
member((ID,X), T),
X = Bound,
vt(VT1, T, NS).
vt(VT, T, NS) :- VT = [unify(ID,ID1) | VT1] |
member((ID,X), (ID1,X1), T),
X = X1,
vt([remove(max(ID,ID1)) | VT1], T, NS).
vt(VT, T, NS) :- VT = [bound(ID,X) | VT1] |
remote(set(ID,X)),
vt([remove(ID)|VT1], T, NS).
vt(VT, T, NS) :- VT =
  [unified(ID,X), unified(ID1,X) | VT1] |
remote(unify(ID,ID1)),
vt([remove(max(ID,ID1)) | VT1], T, NS).

概念的には上のような variable table を作りたい。 NS に ID 空間が枯渇したと言われた場合、自分

自身を広げる。

自分自身を縮めた場合、 NS に ID 空間を縮めろと言う。

6 dklic 実装の課題

6.1 分散論理変数のライフサイクル

1. open. 論理変数を輸出（そして輸入）する。

2. use. 論理変数を項の一部として送受信する。

3. close. 通信路を閉じる。

OpenMessage(id) = {add(id)} CloseMessage(id) =

{(add(id),) remove(id), set(id), unify(id)} OpenMessage(id)の送信者を opener(id), 受信者を openee(id)と呼び、 CloseMessage(id)の送信者を closer(id), 受信者を closee(id)と呼ぶことにする。

分散論理変数の状態遷移

1. ∅(id). 利用されたことがない状態。

(a) ∅(id) → opening(id)

輸出していない id の OpenMessage(id) を遠隔ホストへ輸出する。

(b) ∅(id) → opened(id)

OpenMessage(id)を遠隔ホストから輸入する。

2. opening(id). OpenMessage(id)を輸出したが、遠隔ホストへ輸入されたことを確認していない状態。

(a) opening(id) → opened(id) 輸出した id を遠隔ホストから受信する。

(b) opening(id) → closing(id)

輸出した id の CloseMessage(id)を遠隔ホストへ送信する。

(c) opening(id) → closed(id)

輸出した id の CloseMessage(id)を遠隔ホストから受信する。返事が必要。

3. opened(id). OpenMessage(id)を遠隔ホストから輸入した状態。

(a) opened(id) → closing(id) CloseMessage(id)を遠隔ホストへ送信する。

(b) opened(id) → closed(id)

CloseMessage(id)を遠隔ホストから受信する。返事が必要。

4. closing(id). CloseMessage(id)を送信したが、遠隔ホストへ受信されたことを確認していない状態。 (a) closing(id) → closed(id)

CloseMessage(id)を遠隔ホストから受信する。返事は不要。

5. closed(id). CloseMessage(id)を遠隔ホストから受信した状態。

(a) closed(id) → opening(id) OpenMessage(id)を遠隔ホストへ再輸出する。

(b) closed(id) → opened(id) OpenMessage(id)を遠隔ホストから再輸入する。

実際には ∅(id) = closed(id) である。

6.2 誰が ID を再利用するか

1. closee(id). ↔ 双方 closer(id)の場合どうするか。 ill-moded なプログラムを許したいので、この案は採用しない。

2. openee(id). openee(id)は必ず一人であるので衝突はない。 ID の利用権が交互に移動する。 ↔ openee(id)が id を再利用する前に closed(id) 状態になると、 id を再利用する機会は失われてしまう (id leak)。この案は採用しない。

3. opener(id). ID 空間の利用権を持っている側が常に ID を再利用する。再利用以外の場合と整合しているので、この案を採用する。

次の通信路 S は、ローカルホストと遠隔ホストから交互にメッセージが流れるストリームである。流れるメッセージは well-moded であるが、防御的でないプログラムではストリームを構成するコンスが ill-moded になってしまう。

S = [X1, Y1, X2, Y2, ..]
-> _0 = [x1, _2, x2 | _0]
<- _0 = [_1, y1 | _0]

双方向から同時に書き込まれる well-moded でない通信路があり得るので、通信路の ID を一般に即時再利用することはできない。変数 ID は通信路の ID でなく、通信路の状態 (continuation) の ID である。

ID をいつ再利用できるかを知るために、遠隔ホストからのメッセージによって (id, var) を削除した closee(id)は、返事として ‘ id= ’ を closer(id)に送信する。返事を受け取ったら、 closer(id)は (id, var) を削除して良い。いずれにせよ、再利用権を持つ側がclosed(id)状態で id を再利用する。

返事を受け取るまで、メッセージの送信側は (id, var) を削除してはならない。

local                               remote
                      |
S=[x1,Y1,x2|S1]       |       S=[X1,y1|S1]
----------------------+-------------------
(_0,S)                              (_0,S)
(_2,Y1) _0=[x1,_2,x2|_4] ->         (_1,X1)
(_4,S1)            <- _0=[_1,y1|_3] (_3,S1)
----------------------+--------------------
(_0,S)  -> _0=[x1,_2,x2|_4]         (_0,S)
                   _0=[_1,y1|_3] <-
----------------------+-------------------
(_0,S)  <- _0=[_1,y1|_3]            (_0,S)
                _0=[x1,_2,x2|_4] ->
----------------------+-------------------
(_0,S)  _0=[_1,y1|_3] <-            (_0,S)
(_2,y1)         -> _0=[x1,_2,x2|_4] (_1,x1)
                                    (_3,S1)
----------------------+--------------------
S=[x1,y1,x2|S1]       |     S=[x1,y1,x2|_4]
     _3=[x2|S1]       |          S1=[x2|_4]

6.3 NEW の不要性

‘NEW id’ は、抹消と再利用 ( ‘ id’ = ‘ ’ , ‘ id’ ) によって置き換えることが可能である。従って、 ‘NEW ’ は不要であり、採用しない。

すなわち、 add(id) ∈ CloseMessage(id) ∩ OpenMessage(id) であるので、 ‘NEW id’ の受信側は → closed(id) → opened(id) の遷移をアトミックに行うことが可能であり、 add(id) ∈ CloseMessage(id) に対する返事を省略するような、通信の最適化が可能である。

エントリ (id, var) が不在・消しかけ・登録済みの各状態で遠隔ホストから ‘NEW id’ / ‘ id’ を受け取った場合、

1. ∅(id). 不在

• ‘NEW id’ を受け取ったらエントリを登録する。

• ‘ id’ を受け取ったら実装のバグである。

2. closing(id). 消しかけ

• ‘NEW id’ を受け取ったらエントリを登録する。エントリをローカルホストで消し、遠隔ホストで即時再利用された場合である。

• ‘ id’ を受け取ったら var と解釈する。エントリをローカルホストで消し、遠隔ホストで消される前に送信された場合である。

3. opening(id). 登録済み

• ‘NEW id’ を受け取ることはない。ローカルホストと遠隔ホストで同一 ID を異なる分散論理変数に発行してしまった場合である。

• ‘ id’ を受け取ったら var と解釈する。

7 通信最適化の階級

1. ‘NEW id’ ∉ CloseMessage(id). 通信の最適化は行わない。 ‘NEW id’ によって id を close せず、 CloseMessage(id)を受信したら返事をする。例外は closing(id)状態のときで、返事をすると返事に対する返事が連鎖してしまう。 2. ‘NEW id’ ∈ CloseMessage(id). 通信の最適化

を行う。 ‘NEW id’ によって id を reopen = close + open する。 ‘NEW id’ を送信することができるのは、

(a) closed(id)状態の時。弱い最適化。 CloseMessage(id)を受信して closed(id) 状態になった closee(id)は、返事を省略して ‘NEW id’ を送信することができる。 → closed(id) → opening(id) と状態遷移する。 ‘NEW id’ の受信側 = closer(id)は、 ‘NEW id’ の受信によって closing(id) → closed(id) → opened(id) とアトミックに状態遷移する。

(b) opening(id), opened(id)状態の時。強い最適化。 ID を一般に即時再利用することは不可能であるので、この最適化は不可能である。

local                |              remote
---------------------+---------------------
opening(id) openMessage(id) ->   closed(id)
opening(id)   -> openMessage(id) opened(id)
---------------------+---------------------
closing(id)       <- message(id) opened(id)
opening(id) reopenMessage(id) ->
---------------------+---------------------
opened(id) message(id) <-        opened(id)
    -> reopenMessage(id) closed(id)
                         opened(id)

7.1 NEW のメリット

1. ‘NEW id’ の受信側は id が未使用であることを前提として良い。 id が使用されていたら実装のバグである。従って、ある種のエラー検出が可能となる。 ↔ ‘NEW id’ の送信側は id が未使用であることの検査を必要とする。エラー検出を行うなら、受信側でも id が未使用であることを検査しなければならない。

2. → closing(id) → opening(id) とアトミックに状態遷移することが可能である。 ↔ アトミックに状態遷移することが必要か? 必要であるとしても、 io が抹消と再利用 ( ‘ id’ = ‘ ’ , ‘ id’ ) をアトミックに扱えば良い。

結論として、 ‘NEW ’ のメリットはない。従って ‘NEW ’ は採用しない。

8 参考文献

1. Ueda, K. and Chikayama, T., Design of the Kernel Language for the Parallel Inference Machine. The Computer Journal, Vol. 33, No. 6 (1990), pp. 494–500.

2. 瀧和男編, bit 別冊第五世代コンピュータの並列処理. 共立出版, 1993.

3. Chikayama, T., Fujise, T. and Sekita, D., A Portable and Efficient Implementation of KL1. In Proc. 6th Int. Symp. on Programming Language Implementation and Logic Programming (PLILP’94), LNCS 844, Springer-Verlag, Berlin, 1994, pp. 25–39.

4. 関田大吾: Inside KLIC Version 1.0. KLIC Task Group / AITEC / JIPDEC, 1998. http://www.klic.org/software/klic/inside/master.html

5. Ueda, K., Linearity Analysis of Concurrent Logic Programs. In Proc. Int. Workshop on Parallel and Distributed Computing for Symbolic and Irregular Applications, Ito, T. and Yuasa, T. (eds.), World Scientific, 2000, pp. 253–270.

6. 網代育大, 上田和紀, Kima: 並行論理プログラム自動修正系. コンピュータソフトウェア別冊ソフトウェア発展, Vol. 18, No. 0 (2001), pp. 122– 137.

7. 加藤紀夫, 上田和紀, 並行論理プログラムにおける逐次実行部分の抽出方法論. In Proc. JSSST Workshop on Programming and Programming Languages (PPL2001), 2001.

8. 五十嵐宏, 分散 KL1 言語処理系の設計と実装. 早稲田大学理工学部情報学科卒業論文, 1999.

9. 五十嵐宏, メタインタプリタに基づく分散並行論理型言語処理系. 早稲田大学大学院理工学研究科修士論文, 2001.

10. 高木祐介, KL1 による分散 KL1 言語処理系の実装. 早稲田大学理工学部情報学科卒業論文, 2000. http://www.ueda.info.waseda.ac.jp/~takagi/

11. 高木祐介, 上田和紀, dklic: KL1 による分散 KL1 言語処理系の実装, In Proc. JSSST Workshop on Systems for Programming and Applications (SPA2001), 2001. http://www.dcl.info.waseda.ac.jp/SPA2001/

12. 高山啓, 並行論理型言語 KL1 による分散 KL1 言語処理系の実装. 早稲田大学理工学部情報学科卒業論文, 2001.

13. 松村量, 並行論理型言語 KL1 の分散拡張のための遠隔ノード管理の実装. 早稲田大学理工学部情報学科卒業論文, 2001.

14. Van Roy, P., Haridi, S., Brand, P., Smolka, G., Mehl, M. and Scheidhauer, R., Mobile Objects in Distributed Oz. ACM Transactions on Programming Languages and Systems, Vol. 19, No. 5, September 1997, pp. 804–851.

15. Haridi, S., Van Roy, P., Brand, P. and Schulte, C., Programming Languages for Distributed Applications. New Generation Computing, Vol. 16, No. 3 (1998), pp. 223–261.

16. Haridi, S., Van Roy, P., Brand, P., Mehl, M., Scheidhauer, R. and Smolka, G., Efficient Logic Variables for Distributed Computing. ACM Transactions on Programming Languages and Systems, Vol. 21, No. 3, May 1999, pp. 569–626.

17. R.C. ホルト他, 湯浅太一監訳, 松田元彦, 三好博之, 竹岡尚三訳, プログラミング言語 Turing – 言語仕様の基礎理論と設計技法. 啓学出版, 1990. DKLICP 言語の形式文法を階級分類する参考にした