プログラミング言語は手続きを記述する。世界を叙述できない。


プログラミング言語は人類最高の発明の一つである

プログラミング言語は曖昧でない。 x = 3 + 4はいつどこで実行しても7である。 解釈の余地がない。

プログラミング言語は検証可能である。 構文エラーはコンパイル前に検出される。 型エラーは実行前に検出される。 テストが実行されれば、結果は成功か失敗かのどちらかである。

プログラミング言語はチューリング完全である。 計算可能なものはすべて表現できる。 十分な時間とメモリがあれば、どんな手続きも記述できる。

このシリーズで自然言語の限界として指摘したもの――曖昧さ、検証不可能性、構造の欠如――をプログラミング言語はすべて解決した。

ならばプログラミング言語でAIのコンテキストを表現すればよいのではないか。

できない。


プログラミング言語は手続きを記述する

以下は有効なPythonコードである。

def calculate_revenue(units_sold, unit_price):
    return units_sold * unit_price

このコードは明確で、検証可能で、実行可能である。 しかしこのコードが表現しているのは何か。

「販売数量に単価を掛けると売上になる。」

これは手続きである。方法である。HOWである。 入力が来たら何をするかを記述している。

では次を表現してみよう。

「サムスン電子の2024年第3四半期売上は79兆ウォンである。」

これは手続きではない。事実である。WHATである。 何も実行しない。世界の状態を叙述している。

Pythonでこれをどう表現するか。

samsung_revenue_2024_q3 = 79_000_000_000_000

変数に数値を代入した。 動作する。しかしこれは叙述ではない。格納である。 このコードは以下を知らない。

  • 「サムスン電子」がどのような種類のエンティティか。
  • 「売上」が何を意味するか。財務指標か、物理量か。
  • 「2024年第3四半期」が時間か、バージョンか、ラベルか。
  • 79兆ウォンという値の出典は何か。
  • この値がどれほど確かか。

変数名samsung_revenue_2024_q3は人間が読めば意味を推測できる。 機械にとってこれは任意の文字列である。 xyzzy_42に変えても実行結果は同じである。

プログラミング言語において変数名は意味を持たない。 意味はコードの外、プログラマーの頭の中にある。


より精巧にしても同じである

クラスを作ればよいのではないか。

class FinancialReport:
    def __init__(self, company, metric, period, value, currency):
        self.company = company
        self.metric = metric
        self.period = period
        self.value = value
        self.currency = currency

report = FinancialReport("삼성전자", "매출", "2024-Q3", 79_000_000_000_000, "KRW")

改善された。構造ができた。 しかし問題は残る。

companyは文字列「삼성전자」(サムスン電子)である。 「Samsung Electronics」も「SEC」も「005930」も同じ会社である。 コードはこれを知っているか。知らない。 文字列が同じか異なるかを比較できるだけである。

metricは文字列「매출」(売上)である。 「매출」と「매출액」と「revenue」は同じものか異なるものか。 コードは知らない。文字列が異なるから異なるものである。

スキーマを定義すればよいのではないか。 Enumで企業リストを管理し、指標リストを管理する。 できる。動作する。

では次を表現してみよう。

「李舜臣は偉大であった。」

opinion = Opinion("이순신", "was", "위대했다")

これは何か。 文字列「이순신」(李舜臣)と文字列「위대했다」(偉大であった)を結びつけたものである。 「李舜臣は偉大であった」を表現したのではなく、 「이순신」と「위대했다」を格納したのである。

「위대했다」(偉大であった)の意味をコードは知らない。 「위대했다」(偉大であった)と「훌륭했다」(優れていた)が類似しているか、 「비겁했다」(卑怯であった)が反対であるか、 コードには判断できない。

財務データのような定型的事実はなんとか扱える。 評価、文脈、関係、抽象的叙述はプログラミング言語の表現範囲の外にある。


コードは自分が何をするコードか知らない

def process(data):
    result = []
    for item in data:
        if item["value"] > threshold:
            result.append(transform(item))
    return result

このコードは完璧に実行される。 しかしこのコードは何をするコードか。

売上データをフィルタリングするコードか。 患者記録を選別するコードか。 センサーデータを精製するコードか。

コード自体は知らない。 datavaluethresholdtransform――すべて抽象的な名前である。 このコードが財務システムの一部か医療システムの一部かは、 コード外の文脈に依存する。

コメントで書くことはできる。 しかしコメントは自然言語である。機械は理解しない。 コメントがコードと矛盾していてもコンパイラは気づかない。 コメントは人間のためのものであり、機械のためのものではない。

AIがコードをコンテキストとして受け取るとき、この問題が直接的に現れる。 コードが自己同一性を持っていないため、 AIはその都度推論で同一性を再構成しなければならない。 推論であるから、コストがかかり、誤る可能性がある。


根本的な理由

プログラミング言語が世界を叙述できないのは設計の欠陥ではない。 目的が異なるからである。

プログラミング言語の目的は機械に手続きを指示することである。 「この入力が来たらこの演算を実行せよ。」 プログラミング言語の意味論(semantics)は実行の意味論である。 すべての構文が「機械が何をするか」と解釈される。

x = 3は「xというメモリ位置に3を格納せよ」という指示である。 if x > 0は「xが0より大きければ次のブロックを実行せよ」という指示である。 return xは「xの値を呼び出し元に返せ」という指示である。

すべて動詞である。すべて行為である。すべて手続きである。

「サムスン電子は韓国の企業である」は動詞ではない。 行為ではない。手続きではない。 世界がどのような状態にあるかを記述するものである。

プログラミング言語にはこのための場所がない。 変数に格納することはできるが、それは記述ではなく格納である。 格納された値の意味はコードの管轄外である。


JSON、YAML、XMLはどうか

プログラミング言語ではなくデータ形式はどうか。

{
  "company": "삼성전자",
  "metric": "매출",
  "period": "2024-Q3",
  "value": 79000000000000,
  "currency": "KRW"
}

構造はある。フィールドは明示的である。 しかし意味がない。

「company」が企業を意味するかどうかJSONは知らない。 「삼성전자」が別の場所の「Samsung Electronics」と同一かどうかJSONは知らない。 このJSONオブジェクトとあのJSONオブジェクトが同じエンティティを記述しているかどうかJSONは知らない。

JSONは構造を提供するが意味を提供しない。 キーと値のペアであり、エンティティ・関係・属性ではない。

スキーマを定義すれば改善される。 JSON Schema、Protocol Buffers、GraphQL。 フィールドの型が定義され、必須かどうかが定義され、参照が定義される。

しかしこれらはすべて特定のシステムのために設計された構造である。 汎用的な知識表現ではない。 財務データスキーマで歴史的人物の評価を表現することはできない。 医療データスキーマで企業間の競争関係を表現することはできない。

ドメインごとに別々のスキーマ。 スキーマごとに別々のツール。 スキーマ間の相互運用性がない。

この限界については、なぜMD/JSON/XMLでは駄目なのかでより詳しく論じている。


LISPはどうか

ここまで読んだ人の中には反例を思いついた人がいるだろう。

LISP。

(is 삼성전자 (company korea))
(revenue 삼성전자 2024-Q3 79000000000000)
(great 이순신)

S式はツリー構造であり、 コードがデータでありデータがコードである。 ホモアイコニシティ(homoiconicity)。

実際、初期のAIはすべてLISPベースであった。 SHRDLU、CYC、エキスパートシステム。 LISPで知識を表現し、推論エンジンを動かした。 「プログラミング言語では世界を叙述できない」に対する歴史的反証が存在するように見える。

しかし三つの理由で反例は成立しない。

LISPが知っていることとプログラマーが定めたこと

(is 삼성전자 company)において、isが「~である」という関係を意味することを LISPは知らない。 プログラマーがそう定めたのである。

iszzzに置き換えてもLISPは構わない。 (zzz 삼성전자 company)はLISPにとって完全に有効な式である。

LISPは構造を提供する。S式というツリーである。 しかしその構造の中に込められた意味は言語ではなくプログラマーが付与したものである。 JSONがキーの意味を知らないのと本質的に同じである。

構造を提供することと意味を内蔵することは異なる。

CYCの30年

これを最も野心的に試みたプロジェクトがCYCである。

1984年に開始。 LISPベースで汎用知識を表現しようとした。 数百万のルールが手作業で入力された。

30年が証明したのは可能性ではなく限界であった。

ドメインごとにオントロジーを手作業で設計しなければならなかった。 ドメイン間の相互運用ができなかった。 自然言語の柔軟性についていけなかった。 規模が大きくなるほど一貫性の維持が不可能に近づいた。

LISPで知識表現が「できる」というのは正しい。 「うまくいく」というのは30年の結果が否定している。

evalしないならLISPを使う理由がない

最も根本的な問題。

LISPの力はevalにある。 コードがデータであるからデータを実行できる。 メタプログラミング、マクロ、実行時コード生成。 これがLISPをLISPたらしめるものである。

では(is 삼성전자 company)evalすると何が起きるか。

isという関数に삼성전자companyを引数として渡す関数呼び出しになる。 叙述ではなく実行になる。

知識表現として使うならevalしてはいけない。 evalしないならLISPの意味論を使っていないのである。 S式という構文を借りているに過ぎない。

それは「LISPで世界を叙述する」ことではなく、 「LISPの括弧記法でデータを格納する」ことである。

プログラミング言語としてのLISPの意味論――実行の意味論――は 依然として手続きを記述するためのものである。 構文を借りても意味論は変わらない。


世界を叙述するための言語に必要な条件

プログラミング言語は手続きを記述し、 データ形式は構造を提供するが意味がなく、 LISPですら構文を借りるだけで叙述の意味論を持たない。

世界を叙述するための言語には何が必要か。

エンティティの同一性。 「サムスン電子」が固有の識別子を持たねばならない。「Samsung Electronics」と同一であることを機械が認識しなければならない。文字列比較ではなく、同一性の一致。

関係の表現。 「サムスン電子は韓国の企業である」において「韓国の企業」という関係を表現できなければならない。変数代入ではなく、関係の記述。

叙述の自己記述。 この叙述が何についてのものか、誰が述べたか、いつ時点のものか、どれほど確かかが、叙述自体に含まれていなければならない。コードの外ではなくコードの中に。

ドメイン独立性。 財務データも、歴史的事実も、主観的評価も、抽象的関係も、同じ形式で表現できなければならない。ドメインごとの別々のスキーマではなく、一つの汎用構造。

プログラミング言語はこの四つを持っていない。 プログラミング言語はこのために作られなかったからである。 手続きを記述するために作られた。

自然言語はこの四つをすべてできる。曖昧に。 必要なのは自然言語の表現範囲とプログラミング言語の精密さを結合したものである。


まとめ

プログラミング言語は曖昧でなく、検証可能で、チューリング完全である。 しかし世界を叙述できない。

プログラミング言語は手続きを記述する。 「この入力が来たらこれをせよ。」すべて動詞であり行為である。 「サムスン電子は韓国の企業である」は行為ではない。 プログラミング言語にこのための場所はない。

コードは自己の同一性を知らない。 どのドメインに属するか、どのような目的を果たすか、 コード内に記録されていない。

JSON、YAMLのようなデータ形式は構造を提供するが意味がない。 LISPは構文を借りることができるが叙述の意味論はない。 CYCが30年間LISPベースの知識表現を試み、証明したのは限界であった。

世界を叙述するにはエンティティの同一性、関係の表現、叙述の自己記述、ドメイン独立性が必要である。 プログラミング言語はこのために作られなかった。 自然言語はこれができるが曖昧である。 必要なものは両者の間のどこかにある。