※本稿は資料与えて ChatGPT に作成させたものです。
アラン・ケイは、LISP 1.5 のプログラマー向けマニュアルに掲載されたLISP自身で記述された評価器を「プログラミングのマクスウェル方程式」と呼んだ。彼が見たのは、単に短く美しいプログラムというだけではない。言語の意味を説明する形式が、そのまま実行可能な機械にもなるという、古典数学とコンピューター固有の数学の接点だった。本稿では、マクスウェル方程式の魅力から始め、LISPの誕生、LISP 1.5とFEXPR、そして、ケイ自身の数学観を経て、評価の仕事をオブジェクトへ分配する初期Smalltalkのインタープリタ設計「One Pager」へ至った道筋をたどる。
1. マクスウェル方程式――四つの式が、光の正体を明かす
マクスウェル方程式は、物理を学んでいないプログラマーには、ベクトル解析の記号が並んだ近寄りがたい壁に見えるかもしれない。しかし、その面白さを知るだけなら計算は要らない。四つの式を、電荷、電流、電場、磁場の関係を表す四つのルールとして読めばよい。
第一に、電荷は、電場の湧き出しや吸い込みと関係する。電場を線で表すと、その線は正の電荷から外へ広がり、負の電荷へ集まる。
第二に、磁場を線で表すと、その線には始点や終点がない。磁石を割っても北極だけ、南極だけにはならず、それぞれが北極と南極をもつ二つの小さな磁石になる。
第三に、時間とともに変化する磁場は、その周囲に渦状の電場を生じさせる。磁石をコイルへ出し入れすると電圧が生じ、回路が閉じていれば電流が流れる。これが電磁誘導である。
第四に、電流と、時間とともに変化する電場は、その周囲に磁場を生じさせる。
第三と第四の法則を組み合わせると、電場と磁場の変化が波として空間を伝わりうることが分かる。方程式から計算されるその波の速度は、当時すでに測定されていた光速と一致した。1
このことから、光は電気や磁気と無関係な別種の現象ではなく、電磁場の波として理解できることが示された。
ただし、マクスウェル方程式が、静電気、磁石、電流などを、すべて「電磁波」という一種類の現象へ置き換えたわけではない。電磁波は、四つの法則を組み合わせることで導かれる重要な帰結の一つである。
マクスウェル方程式の価値は、単に式が短いことにはない。
- 一見ばらばらな現象を、共通の理論の中へまとめる
- すでに知られていた現象を説明する
- それまで知られていなかった現象の存在を予測する
- 複数の式が組み合わさり、一つの整合的な世界像を作る
ここでいう「圧縮」とは、多くの現象を一種類の現象へ還元することではない。多様な現象の関係を、人間が見通せる少数の規則として表すことである。
マクスウェル方程式は、数式が単なる計算の道具ではなく、自然界の異なる現象を結びつけ、それまで見えていなかった関係を明らかにしうることを示した。
アラン・ケイがLISPの評価器に見いだしたのも、この種類の圧縮だった。もちろん、物理現象とプログラムが同じ仕組みで動くという意味ではない。少数の互いに結びついた規則が、多様な現象や振る舞いを見通せる形で記述するという、形式上の共通点である。
2. 「数を計算する機械」から「記号を動かす機械」へ
1950年代のコンピューターは、巨大で高価な数値計算機だった。プログラムやデータはパンチカードなどから投入され、結果は後で受け取る。メモリーは小さく、機械ごとの差も大きい。プログラミング言語そのものが、まだ発明の途上にあった。
FORTRANは、科学技術計算の数式を機械語へ変換することに成功した。しかし、初期の人工知能研究者が扱いたかったのは数値だけではない。
- 論理式
- 定理
- 単語と文
- 木構造
- 探索手順
- プログラムそのもの
こうした記号構造を計算の対象にするため、ジョン・マッカーシーは1950年代後半にLISPを構想した。名称は LISt Processor に由来する。プロジェクトの開始は1958年秋であり、その基本的な考え方は1960年の論文で提示された。2
LISPの中心的なデータ構造は、二つの参照を持つコンスセルである。リストは、先頭と残りに分けられる。
(car '(A B C)) ; A (cdr '(A B C)) ; (B C)
二つの要素を組み合わせれば、新しいリストを作れる。
(cons 'A '(B C)) ; (A B C)
決定的なのは、プログラムも同じリスト構造で表されることだ。
(+ 3 4)
これは加算を指示する式であると同時に、記号 + と数値 3、4 からなるデータでもある。したがってプログラムは、別のプログラムから分解、変形、生成できる。
「コードとデータが同じ材料でできている」という性質は、後のマクロやメタプログラミングだけでなく、言語が自分自身について語るための土台になった。
3. 紙上の定義が、インタープリタになった
マッカーシーは、LISPの意味を説明するために eval を定義した。もともとは、S式がどのように値へ変換されるかを示す数学的・記述的な関数だった。
ところがスティーブ・ラッセルは、この定義を機械語で実装すればLISPインタープリタになると見抜いた。マッカーシーは当初その考えに懐疑的だったが、ラッセルはIBM 704上で実際に動かした。3
ケイは、LISPの偉大さの中心はマッカーシーの「アイデア」にあるとしながらも、ラッセルのようにアイデアを現実の機械へ橋渡しした人々を明確に評価している。同じ関係はSmalltalkでも、ケイの設計と、ダン・インガルスによる実装の間に見られる。4
ここで、LISP史を貫く循環が生まれる。
- LISPプログラムはリストとして表せる
- リストはLISPプログラムで操作できる
- LISPプログラムの意味は
evalで定義できる eval自身をLISPで記述できる- その評価器へLISPプログラムを渡せば、LISPが動く
言語の定義が、言語を実行する機械にもなる。
一般的な仕様書は、実装者が別の仕組みに翻訳しなければ動かない。LISPの評価器では、その距離が極端に短くなった。意味論、プログラム、実装の設計図が、ほぼ同じ形を取ったのである。
4. LISP 1.5――研究アイデアが生きたシステムになる
1962年に刊行された LISP 1.5 Programmer’s Manual は、John McCarthy、Paul W. Abrahams、Daniel J. Edwards、Timothy P. Hart、Michael I. Levinの共著である。5
「1.5」という番号は、現代的な小規模アップデートの印象とは異なる。構想されたLISP 2へ向かう途中にありながら、実際に利用できるシステムとして育った版だった。
LISP 1.5には、(1962年の初版マニュアルの時点では)マクロこそまだ無かったが、後のLisp文化を形作る要素がすでにそろっている。 [追記:id:g000001 さんにコメント欄でご指摘いただいた通り、マクロについての記述が正確ではありませんでした。LISP 1.5も1963年にはコンパイラでマクロをサポートしていたそうです。なお、マクロは(後述する) FEXPR を置き換えるために導入され、両者は長らく併存したのち、ついにCommon Lispで FEXPR は淘汰されたのだとのこと。]
- S式によるプログラム表現
- 再帰的なリスト処理
- 条件式
COND - 動的な名前の束縛
- ガベージコレクション
- インタープリタとコンパイラ
- 属性リスト
EXPR、FEXPR、SUBR、FSUBR- Lisp自身で記述された
evalとapply
マニュアル13ページ下部には、evalquote、apply、eval、そして補助関数 evcon、evlis の定義が掲載されている。6
役割を現代的な言葉で単純化すれば、次のようになる。
eval : 式を環境の中で評価する apply : 関数を引数と環境に適用する
eval は式の形を調べる。
- 記号なら環境から値を探す
- 引用なら式をそのまま返す
- 条件式なら条件を順に調べる
- 関数適用なら引数を処理して
applyへ渡す
apply は関数の形を調べる。
- 基本関数なら対応する処理を行う
LAMBDAなら仮引数と実引数を結び、新しい環境で本体をevalする- 再帰的な定義なら、関数名を環境へ加えて適用する
eval が apply を呼び、apply が再び eval を呼ぶ。この小さな循環へ、変数参照、関数適用、条件分岐、再帰、環境が収まる。
もちろん、実際のLISP 1.5処理系全体が半ページだったわけではない。入出力、メモリー管理、機械語ルーチン、コンパイラなど、多くの実装が必要だった。それでも、言語の意味の中心を、人間が一度に見渡せる大きさへ収めたことは画期的だった。
5. 「ソフトウェア(もしくはプログラミング)におけるマクスウェル方程式」
アラン・ケイは大学院時代、LISP 1.5マニュアル13ページ下部のコードが「Lispで書かれたLisp」であることを理解し、大きな衝撃を受けた。
2004年のACM Queue のインタビューで、彼はその半ページを “Maxwell’s Equations of Software!” と表現している。7 一方、後年のQuora回答では「たしか “Maxwell’s Equations of programming” と言ったと思う」と記憶を留保しつつ、この比喩がLispの登場した1950年代末から1960年代初頭にはよく当てはまると説明した。そこで強調されているのは、当時行われていた種類のプログラミングを包含する強力なモデルを “one eyeful”――一目で見渡せる大きさ――で示したことだった。8
別のQuora回答でケイは、これをさらに “unlimited programming in an eyeful”(「ひと目で見渡せる、万能のプログラミング」)と言い換えている。彼が「Lispは史上最高の単一のプログラミング言語設計だ」と称賛した理由は、括弧を使う外見や特定の実用性能ではない。マッカーシーが、ほぼあらゆるプログラミング言語を表現できる一般性を備えながら、その本質を小さなメタ記述として表現したことにあった。4[^kay-greatest]
したがって、後のケイの意図を汲むなら “programming” だが、“Software” でも意味は通りそうだ。無理にどちらか一方を唯一の定型句として固定する必要はないだろう。比喩の中心は、当時のプログラミングを広く包含するモデルを、人間が一度に把握でき、しかも実行へ移せる形に圧縮した点にある。
この比喩は、次の三点でよく働く。
①多数の機能が、一つの原理として見える
関数呼び出し、変数探索、条件分岐、再帰は、利用者から見ると別々の機能である。eval と apply の相互作用として見ると、すべて「式を環境の中で意味へ変える」という一つの過程になる。
②記述が予測と生成の力を持つ
評価規則を少し変えれば、異なる言語が生まれる。環境の扱い、引数評価の時期、関数の表現を変更すると、新しい計算モデルを実験できる。評価器は既存のLISPを説明するだけでなく、LISPに似た別の世界を作るための母型にもなる。
③全体を思考の対象にできる
ケイは、人間の思考を制約する認知負荷に繰り返し言及している。いちどに注意を向けられる対象が少ないなら、その少数の記号へ、より大きな意味を担わせなければならない。彼の標語 “Point of view is worth 80 IQ points” は、新しい表現形式が、以前は難しすぎた思考を可能にするという意味である。4[^kay-greatest]
LISPの半ページの評価器は、処理系の核心を一度に視野へ収める。そのうえで手で追跡し、変更し、別の言語を考えられる。ケイが称賛したのはLISPの完成度だけではなく、LISPを理解した者が、ただちにLISPよりよい言語や、よりよいメタ記述を構想できるようになる視点だった。
6. ケイの数学観――形式は、動いてこそ試される
ケイは数学的厳密さを拒んでいたのではない。むしろ、プログラミングシステムの問題は新しい種類の数学的問題だと考えていた。
1998年のメーリングリスト投稿で、彼は古典的な数学を CM(Classical Mathematics)、コンピューター固有の数学を CptrM(Computer Mathematics) と呼び分けている。そこでマッカーシーの自己記述的LISP評価器を、両者の世界にまたがる美しい数学的対象として挙げた。9
この区別は、「数式かコードか」という表面上の違いではない。
古典数学では、公理と定義を置き、命題を導く。形式は主として静的な対象であり、その妥当性は証明や人間の読解によって検討される。
コンピューター上の形式には、それに加えて次の性質がある。
- 入力を与えて実行できる
- 状態が時間とともに変化する
- 他の仕組みと相互作用する
- 途中経過を観察できる
- 想定外の例を試し、欠陥を発見できる
- 形式自身を部品として別の形式を構築できる
プログラムは記述であると同時に、実験装置でもある。
ケイは、古典数学の形式をコンピューターへ外から被せるだけでは、この媒体の特徴を十分に捉えられないと考えた。彼が問題視したのは数学そのものではなく、形式が巨大で理解しにくく、しかも実行不能なまま正しさを保証したことになる状況だった。
アルゴリズムを完全に記述する形式が、元のプログラムより簡潔で明晰なら、なぜその形式を直接実行しないのか。速度が足りなければ、最適化を機械に任せればよい。ケイの批判は、この問いに集約できる。
推論機械であり、考えるための物体でもある
ケイがニクラウス・ヴィルトによるEulerの形式化を高く評価した理由も同じである。ヴィルトの仕事では、小さな状態空間言語、そのコンパイラ、バイトコードインタープリタが、数ページの記号へまとめられていた。10
それは言語を説明する仕様であるだけでなく、実行の仕組みであり、人間が全体を眺めて考えられる人工物でもあった。ケイは、形式と機械がこのように近接した仕事を好んだ。
最低層から最高層までの「急勾配」
ケイはLISPについて、単純な機械構造から高水準の表現までの「傾斜」が、かつてないほど急だったとも述べている。4[^kay-greatest] これは、抽象度の高い表現がハードウェアから隔絶しているという意味ではない。少数の基礎機構から、自己記述や言語構築へ一気に上れるという意味である。
この急勾配が、LISPを単なる一言語ではなく、言語について考えるための足場にした。小さな機械的核と大きな表現力が同じ系の中で接続されているため、抽象的なアイデアを実装へ降ろし、実装上の現象を再びモデルとして考えられる。
LISP評価器に残った一つの不足
ケイは同じ1998年の投稿で、マッカーシーの評価器の「唯一の本当の欠点」は、純粋関数的な性格がコンピューターの強力な状態空間を反映していないことだと述べた。9[^kay-formalizing]
なお、この批判は評価器という形式の性格についてのものであり、マッカーシー個人が状態を軽視したという意味ではない。ケイは別の場所で、状態と時間の扱いにおけるマッカーシーの先駆性——変化の履歴を保持し、「擬似時間」で索引づけて関数的・論理的な推論と両立させるsituation calculus——をむしろ高く評価し、PARCでも可能な範囲で取り込もうとしたと述べている。11
ここでいう状態は、単なる代入文の有無ではない。
- ユーザーの操作を待つ
- オブジェクトが過去を記憶する
- 画面上の対象が継続的に変化する
- 一つの操作が後の振る舞いを変える
- 複数の主体がメッセージを交換する
ケイが構想したパーソナルコンピューターは、一度入力して答えを得る関数計算機ではなく、人間と長時間対話する動的な媒体だった。そのため、状態と時間は排除すべき不純物ではなく、正面から扱うべき計算の構成要素だった。
課題は、関数的な明晰さを捨てずに、状態を含む実行モデルを小さく記述することだった。実際ケイは同じ投稿で、Smalltalk-72の黎明期に自分が試みたのは、マッカーシーの評価器に劣らず小さく明晰でありながら、状態空間を備え、読みやすい構文を許すインタープリタ定義だったと振り返っている。9[^kay-formalizing] この状態の軸に答える部品が、後述するメッセンジャーである。
ただし、ケイのLISP批判にはもう一つ、これとは独立した軸があった。次章で見るFEXPRが関わるのは、そちらの軸である。
7. FEXPR――受け手が引数の読み方を決める
LISP 1.5には、通常の関数とは異なる評価規則を持つ FEXPR があった。これは古いLISPにおけるマクロの前身のような機能で、特殊形式を定義することができた。
通常の関数( FEXPR に対して EXPR )では、引数が先に評価され、得られた値が関数へ渡される。
(f (+ 1 2))
この場合、f が通常の関数なら、まず (+ 1 2) が評価され、値 3 が渡る。
FEXPRには、引数を構成する式が未評価のまま渡される。FEXPR側は、その式をどう扱うか自分で選べる。
- 評価しない
- 一度だけ評価する
- 一部だけ評価する
- 条件を見て必要な式だけ評価する
- 別の環境で評価する
これは利用者定義の特殊形式、ことに制御構造を作るうえで強力だった。たとえば条件分岐では、真の場合と偽の場合の両方を先に評価してはならない。未評価の式を受け取れれば、条件を調べた後で必要な側だけを実行できる。
LISP 1.5では、LISPで定義された通常関数を EXPR、未評価引数を受け取る関数を FEXPR と分類した。機械語で実装された対応物は SUBR と FSUBR である。12
FEXPRが示したのは、呼び出し側や中央評価器だけでなく、受け手も評価の時期へ関与できるという可能性だった。
ただし、自由の範囲は中央の eval が決めている。評価器が関数の種類を調べ、「FEXPRなら未評価で渡す」と分岐する。受け手の自由は、評価器が用意した特別な入口から与えられていた。
ケイの不満は、まさにこの構図に関わる。The Early History of Smalltalk での回想によれば、純粋なLISPは関数に基づくとされながら、lambda式、quote、condといった最重要の構成要素は関数ではない「特殊形式」という例外だった。他の研究者の工夫で一部を関数に還元できても、宝石に残った傷は消えない。一方、実用言語の側には、引数を評価するEXPRと評価しないFEXPRが最初からある。それならなぜ関数型言語と名乗るのか。いっそすべてをFEXPRに基づかせ、必要な評価は受け手側で強制すればよいのではないか。 この問いに良い答えは得られなかったが、Smalltalkを発明する際に大いに役立った、とケイは述べている。13
副作用や状態の扱い(前章)とは別の、この統一性の軸が、FEXPRの入口を評価機構全体へ広げるという発想の出所である。
8. 中央の eval を、オブジェクト社会へ分配する
オブジェクト指向言語を考えるケイにとって、LISPの評価器は美しい一方、意味の判断が中央へ集まりすぎていた。
eval は、式を見て次のような判断を行う。
- 変数か、リテラルか
- 引用か、条件式か
- 引数を先に評価するか
- どの環境で評価するか
- 関数をどう適用するか
新しい評価方法を追加するには、この中央機構との関係を決めなければならない。
ケイは逆の構造を考えた。
- 数は、
+の意味を自分で決める - 真偽値は、続く式を評価するか自分で決める
- 変数は、値の参照や代入をメッセージとして処理する
- 引数受信用のオブジェクトは、次の式を評価するか否かを決める
- 制御構造は、実行コンテキストへ保護された方法で働きかける
- クラスのコードは、受け取れるメッセージの形を記述する
添付資料の元となった The Early History of Smalltalk の付録で、ケイはこの考えを、eval を個々のオブジェクトと、言語が拡張される動的過程へ distribute することだと説明している。14
One Pagerの短さは、処理を消した結果ではない。中央評価器が担っていた役割を、オブジェクト、クラスコード、実行コンテキストへ移した結果である。
9. 1972年の賭け――最強の言語を1ページで
1972年、Xerox PARCでケイ、ダン・インガルス、テッド・ケーラーが、プログラミング言語の力について議論していた。ケイは、世界で最も強力な言語を1ページのコードで定義できると主張した。反応は当然、「ならばやってみろ」だった。15
ケイにはLISPの先例があった。関数的な言語世界を半ページほどで示せるなら、状態を持つオブジェクト指向の世界も、同程度の明晰さで定義できるはずだ。
彼が目指した条件は、少なくとも三つあった。
- マッカーシーの評価器のように小さく、全体を考えられる
- 状態空間を持つ
- LISPより自然に読める構文を許す
ケイは続く2週間ほど、始業前の早朝に設計を進め、出勤してきたインガルスらが案を批評する、という作業を繰り返した。最初の案には欠陥があったが、そのうち手作業で追跡可能なインタープリタ設計へ到達した。これがOne Pagerである。
ケイはこの話はここで終わりと思っていたが、その後数日で、インガルスが必要なスキャナやリスト処理などを補い、BASICでNOVA上に実装してしまい、ケイを驚かせたという。3+4 の評価は非常に遅かったが、結果は 7 になった。15[^earlyst-bet]
One Pagerはケイによるインタープリタ設計であり、それを最初に実機上の処理系として動かしたのはインガルスだった。マッカーシーの eval がラッセルにより実装されたエピソードを彷彿とさせるこの処理系誕生と続く改良が、後にSmalltalk-72と呼ばれるシステムへ育っていく。
10. One Pagerの発想――手紙ではなく、手紙の場所を知らせる
現代的なオブジェクト指向言語では、メッセージ送信を次のように考えることが多い。
- 引数を評価する
- セレクタに対応するメソッドを探す
- メソッドを実行する
- 戻り値を返す
One Pagerのモデルは異なる。
ケイは、送信を「郵便屋が手紙を届けること」ではなく、手紙がどこにあるかを受け手へ通知することになぞらえた。14[^earlyst-appendix]
受け手は、送り手が保持しているメッセージ列の位置へアクセスし、必要な分だけ読み進める。
- 次のトークンが特定の記号か調べる
- 次の部分式を送り手の環境で評価する
- 次の部分式を未評価のまま受け取る
- 不要な部分を飛ばす
- 独自の文法として解釈する
- 結果を送り手へ返す
送信時点でメッセージの構造と評価順序が完全に確定するのではない。受信側が読み進める過程で意味が形になる。
11. 3+4 =「3へ +4 を送る」は単なる比喩ではない
One Pagerでは、式の最初の要素を評価して受け手とし、残りをメッセージとして扱う。
receiver | message
3 | + 4
+ は、インタープリタが生まれつき知っている固定演算子である必要はない。整数クラスが受け取れば整数加算として、行列が受け取れば行列加算として、文字列が受け取れば連結として解釈できる。
さらに受け手は、4 をいつ、どの範囲まで、どの環境で評価するかに関与する。
この設計では、構文解析、引数評価、メソッド選択が完全に分離した工程ではない。それらは「受け手がメッセージを読み取る」という一つのプロトコルへ組み込まれている。
12. 「メッセンジャー」――状態と制御を対象化するオブジェクト
One Pagerの中心にあるのは、メッセンジャー(MESSENGER)と呼ばれる実行コンテキストである。概略として、次の情報を持つ。
SNDR/SENDER 送り手のメッセンジャー GLOBAL 大域環境 SELF 現在の受け手 PC メッセージ中の現在位置 MSG/MESSAGE 現在解釈しているメッセージ列 RTN/RETURN 評価終了後の復帰先
この表記はOne Pagerの擬似コード自体でも用いられている。RTNには、このメッセンジャーの評価が終わった後に実行される継続のラベルが入る。図の APPLY はその一つで、擬似コードにはほかに条件実行からの復帰用 FROMTRUE などがある。14[^earlyst-appendix]
現代的に言えば、環境、スタックフレーム、継続、プログラムカウンタ、メッセージカーソルを組み合わせたような存在である。
重要なのは、実行状態が処理系内部の見えない機構だけに閉じ込められず、オブジェクトとしてモデル化されていることだ。制御構造や引数受信オブジェクトは、保護された方法でメッセンジャーへ働きかけられる。また、メッセンジャーとの協働により、Smalltalk-72 環境には、すでにインタラクティブな「検査機能」として強力なデバッグ機能が備わっていた。16
これにより、LISPの eval 内部に置かれていた環境操作や制御の一部を、オブジェクト世界の側へ移せる。
評価の二場面
添付された二つの図は、3+4 の評価で実行コンテキストがどのように切り替わるかを示している。両図にある e(Beforeでは左上に、Duringでは下段のメッセンジャーの左脇に置かれている)は、One Pagerの冒頭で「現在のメッセンジャーオブジェクトに束縛される環境(the environment)」と定義されているレジスタである。14[^earlyst-appendix] Beforeでは送り手のメッセンジャーを、Duringでは新しく作られた受け手側のメッセンジャーを指しており、活性コンテキストの移動を表す。なお原図では図中のこのラベルは判別しがたいため、e の文字は読み解きの補助として補ってある。
Before――送り手が式の途中にいる
3+4 評価開始時のメッセンジャー
送り手側メッセンジャーの MSG は、3、+、4 を含むコード列を指す。列の先頭側にある黒点は、すでに消化されたトークンを表す。評価はメソッドの途中から始まっているのである。PCが 3 を指しているとき、eval はそれを整数オブジェクトとして評価する。得られた整数 3 が、残りのメッセージを受け取る SELF になる。
During――整数クラスのコードがメッセージを読む
新しいメッセンジャーでは SELF が整数 3 となり、MSG は整数クラスのコードを指す。一方、読み取る対象である + 4 は送り手側のメッセージ列に残っている。図の点線が示すとおり、この時点で送り手側のPCは、消費済みの 3 の次のトークンである + を指す。Beforeで 3 を指していたPCは、その消費とともに一つ進んでいるのである。
受け手側コードの ¤ のような補助オブジェクトは、送り手側のPCを手がかりにメッセージ列を調べ、受信の進行に応じて送り手のPCを動かす。ケイ自身、こうした補助オブジェクトは送り手のプログラムカウンタを「責任を持って(responsibly)」動かさねばならないと注記している。14[^earlyst-appendix] 実行主体は受け手へ移っているが、受信対象は送り手の文脈に保持される。この二つのコンテキストの協調が、遅延されたメッセージ受信を可能にする。
13. 小さな受信プロトコル
初期設計では、メッセージ列を読むための少数のオブジェクトまたはメタ操作が使われた。14[^earlyst-appendix]
| 記号 (Kay) |
記号 (ST-72) |
記号 ASCII |
名称 | 役割 |
|---|---|---|---|---|
¤ |
∢ |
% |
eyeball | 次のトークンが指定されたリテラルか調べる |
: |
: |
: |
evl-bind | 次の部分を評価し、結果を束縛する |
⦂ |
⦂ |
; |
unval-bind | 次の部分を未評価のまま束縛する |
^ |
⇑ |
! |
send-back | 値を送り手へ返す |
' |
☞ |
" |
quote | メタ解釈を抑止する |
Duringの図で整数クラスのコード列が ¤ + » で始まっていたことを思い出そう。»(Smalltalk-72 では ⇒ )は、ケイの付録で「最初の『実際の』実装時に定義された」記号群(to、ISNEW、=、»、.)の一つで、then と名づけられた条件実行の記号である。受け手が真なら、メッセージの次の部分を評価してそこで抜け、偽なら次の部分を読み飛ばして評価を続ける。14[^earlyst-appendix] 整数クラスのコードは「次のトークンが + なら、加算の処理へ進む」という受信パターンを、¤ と » の連なりで表現している。同じ形は、Smalltalk-72で書かれたclass integerによるfactorialの定義など、初期のコード例にも一貫して現れる。17
: と ⦂ の対比に注目すると、FEXPRとの関係が見える。
LISP 1.5では、関数全体を EXPR または FEXPR と分類し、引数を評価して渡すか、未評価で渡すかを決める。
One Pagerでは、受信コードの中で、メッセージの各部分について選べる。
- 次のトークンはリテラルとして照合する
- 次トークン以降を式として送信側の文脈で評価して束縛する
- 次のトークンは未評価のまま束縛する
評価方法の選択が、関数単位からメッセージ文法の部品単位へ細分化された。
なお、現存するOne Pagerは1972年の原稿そのものではなく、ケイが後年、失われた設計を記憶から再構成したものである。ケイ自身も、手作業でいくつかの例を評価して動くように見えるが、バグがあるかもしれないと記している。14[^earlyst-appendix]
14. ペアオブジェクトが、自分の構文を読む
c がLISPのコンスセルに似たペアオブジェクトだとする。
c hd ← 3 + 4
受信側のコードは、概念的に次の順序でメッセージを読む。
- 次のトークンは
hdか - 続くトークンは代入記号
←か - 残りの式
3+4を送り手の環境で評価する - 結果を内部変数
hへ格納する - その値を送り手へ返す
LISP風に書けば、おおよそ次に相当する。18
(rplaca c (plus 3 4))
ただし意味の置き場所が異なる。
LISPでは、評価器が式の構造を読み、RPLACA を適用する。One Pagerでは、c のクラスコードが hd ← ... という受信パターンを認識し、必要な部分の評価を依頼する。
構文は独立したパーサーだけが所有するのではなく、受け手の振る舞いの一部になる。
15. FEXPRからOne Pagerへ――自由の粒度を細かくする
三つのモデルを並べると、発想の移動が分かりやすい。
通常のLISP関数
評価器が引数を評価する
↓
関数が値を受け取る
FEXPR
評価器が関数の種類を判定する
↓
未評価の引数列を渡す
↓
FEXPRが必要に応じて評価する
One Pager
受け手へメッセージの所在を知らせる
↓
受け手側コードが列を読み進める
↓
部分ごとに照合・評価・未評価束縛・スキップを選ぶ
ケイがFEXPRから得たのは、単に「引数を遅延評価できる」という機能ではない。受け手が、自分へ届くプログラム断片の意味形成へ参加できるという考えだった。
不足していたのは、その能力が中央評価器の特別分岐として与えられていたことだ。One Pagerは、FEXPR的な自由を通常のメッセージ受信へ一般化しようとした。
こうして、ケイの二つのLISP批判は、One Pagerの別々の部品で回収される。特殊形式という例外への不満(統一性の軸)には受信プロトコルが、純粋関数性が状態空間を映さないという不満(状態の軸)にはメッセンジャーが、それぞれ答えているのである。
16. オブジェクトは、状態を理解可能な大きさへ区切る
ケイの古典数学批判とオブジェクト指向は、ここで接続する。
巨大なプログラムの全状態を一つの形式へ展開すると、記述は急速に大きくなる。形式的であっても、人間が全ケースを理解できなければ、その価値は限定される。
オブジェクトは、状態と、その状態を扱うコードを局所化する。
- 内部状態へ直接触れさせない
- 外部との関係をメッセージへ限定する
- 誤りや変更の影響範囲を狭める
- 各部分を、人間が考えられる大きさに保つ
これは単なるコード整理術ではない。複雑な動的システムについて推論するための構造でもある。
One Pagerでは、この原則が言語処理系自身へ適用された。評価器を巨大な中央機構として完成させる代わりに、評価、構文、制御、状態の役割を、相互作用する対象へ分ける。
ケイのいうCptrMは、古典数学を捨てた無形式なプログラミングではない。実行でき、人間が追跡でき、修正の影響を局所的に考えられる形式を作ることだった。
17. 後のSmalltalkとは異なる、受信言語としてのSmalltalk-72
One Pagerから始まるSmalltalk-72は、Smalltalk-76やSmalltalk-80と同じ実行モデルではない。
後のSmalltalkでは一般に、構文が解析され、引数が評価され、セレクタに対応するメソッドがクラス階層から検索される。
Smalltalk-72では、クラスコードがメッセージ列をパターンマッチし、必要な部分を能動的に読み取る。クラスはメソッド辞書というより、小さな受信言語を持つインタープリタに近い。
この方式は柔軟だが、実行効率、理解しやすさ、ツール支援、最適化の面で重い。Smalltalkの設計はその後ダン・インガルス主導で大胆に整理され、Smalltalk-76では、今日知られるメソッド辞書とバイトコードインタープリタに近い構造へ移っていった。19
One Pagerの価値は、現在のオブジェクト指向言語を最小化したことではない。「受け手はメッセージの意味をどこまで決められるか」という問いを、動く処理系の形で試したことにある。
18. 半ページと1ページ――継承と反転
LISP 1.5の評価器とOne Pagerには、明確な継承関係がある。
ケイがLISPから受け継いだのは、次の考えだった。
- 言語の意味を小さな評価器へ圧縮できる
- コードを通常のデータとして扱える
- 評価規則自体をプログラムから操作できる
- 実行環境を明示的な構造として考えられる
- 定義は実装から切り離された説明にとどまらず、実行可能になり得る
そのうえで、二つの点を反転させた。
第一に、純粋関数的な世界へ状態を導入した。
第二に、意味を決める場所を中央の eval からメッセージの受け手へ移した。
LISP:
evalが式を読み、評価方法を決める
初期Smalltalk:
受け手がメッセージを読み、受信方法を決める
One PagerはLISP評価器の否定ではない。その美点を、別の計算世界へ移植する試みだった。
19. 結び――コンピューター自身を数学の媒体にする
マクスウェル方程式は、多様な物理現象を少数の法則へ統一した。LISPの自己評価器は、多様な言語機能を eval と apply の相互作用へまとめた。
ケイが特に高く評価したのは、その形式が実行可能だったことである。LISP評価器は、言語について説明する記号列であると同時に、実装すればその言語を動かす機械になる。人間は全体を読み、機械は同じ構造を実行する。
この性質は、ケイがコンピューターに見た新しい数学の条件を満たしていた。
One Pagerは、そこへ状態と相互作用を加える試みだった。実行コンテキストをメッセンジャーとして対象化し、FEXPRが示した受け手側の評価能力をメッセージ受信へ拡張し、中央評価器の仕事をオブジェクトへ分配した。
その設計は完成されたSmalltalkの最終形ではない。再構成された記述には未確定な部分があり、後のSmalltalkは異なる実行モデルへ進んだ。それでも、One Pagerは思想を紙上にとどめなかった。インガルスの実装によって動き、Smalltalk-72という実験環境の出発点になった。
ここに、ケイが評価した仕事の基準が表れている。
形式的に美しいこと。人間が全体を考えられること。そして、機械の上で本当に動くこと。
LISP 1.5の半ページからSmalltalkの1ページへ続く道は、プログラミング言語の影響関係だけを示すものではない。それは、コンピューターを古典数学の適用対象として見るだけでなく、状態、時間、相互作用を含む新しい数学を行うための媒体として捉える試みの歴史でもある。
- James Clerk Maxwell, “A Dynamical Theory of the Electromagnetic Field,” Philosophical Transactions of the Royal Society of London, Vol. 155, 1865, pp. 459–512. Royal Society Publishing / IEEE History Center, “Maxwell’s Equations, 1860–1871,” Engineering and Technology History Wiki. ETHW。マクスウェルによる電磁気学の統一と電磁波の導出については両者を参照。↩
- John McCarthy, “Recursive Functions of Symbolic Expressions and Their Computation by Machine, Part I,” Communications of the ACM, Vol. 3, No. 4, 1960, pp. 184–195. Stanford PDF。LISPの初期構想と記号式、再帰関数については本論文を参照。↩
-
John McCarthy, “History of LISP,” in Richard L. Wexelblat (ed.), History of Programming Languages, Academic Press, 1981, pp. 173–185. Stanford version / Paul Graham, Hackers and Painters: Big Ideas from the Computer Age, O’Reilly Media, 2004, p. 185。
evalの実装をスティーブ・ラッセルが提案・実現した経緯は、前者に収められたマッカーシーの回想に記されている。マッカーシーが当初「君は理論と実践を混同している。このevalは読むためのものであって、計算するためのものではない」と懐疑的だった逸話は、後者に収録されたマッカーシー自身の発言による。↩ - Alan Kay, “What did Alan Kay mean by, ‘Lisp is the greatest single programming language ever designed’?”, Quora, edited October 30, 2017, together with Kay’s comments on the answer. Quora answer。認知負荷、“Point of view is worth 80 IQ points”、“unlimited programming in an eyeful”、Lispの「急勾配」、マッカーシーとラッセルの役割については、同回答とそのコメント欄での補足を参照。↩
- John McCarthy, Paul W. Abrahams, Daniel J. Edwards, Timothy P. Hart, and Michael I. Levin, LISP 1.5 Programmer’s Manual, MIT Press, 1962. Computer History Museum / Software Preservation Group PDF。初版の標題紙には5名が著者として記載されている。↩
-
前掲 McCarthy et al., LISP 1.5 Programmer’s Manual。
evalquote、apply、evalの定義は印刷ページ13–14付近を参照。↩ - Stuart Feldman and Charles Simonyi, “A Conversation with Alan Kay,” ACM Queue, Vol. 2, No. 9, 2004. ACM Queue。ケイによる “Maxwell’s Equations of Software!” という表現と、マニュアル13ページへの言及はこのインタビューにある。↩
- Alan Kay, answer to “According to Alan Kay, LISP's metacircular interpreter serves as the ‘Maxwell's equations of software’. What are then the Einstein's field equations of software?”, Quora, edited October 31, 2021. Quora answer。本文中の引用内容はユーザー提供の転記に基づく。↩
- Alan C. Kay, “HELP! formalizing OO,” Squeak mailing-list post, April 26, 1998. Mailing-list archive。CM、CptrM、Lisp評価器の純粋関数性、状態空間、形式手法への批判については、この投稿を参照。↩
- Niklaus Wirth and Helmut Weber, “EULER: A Generalization of ALGOL, and Its Formal Definition,” Communications of the ACM, Vol. 9, No. 1, 1966, pp. 13–25; No. 2, pp. 89–99. ACM DOI。ケイが言及したEulerの形式化はこの論文(CACM 9巻1号・2号に分載)である。↩
- Alan Kay, answer to “Why is functional programming seen as the opposite of OOP rather than an addition to it?”, Quora, 2018年(回答中のCurryOnアムステルダム講演〔2018年7月開催〕および「マッカーシーが60年前に示した」という言及からの推定). Quora answer 本稿ではユーザー提供の転記も参照。マッカーシーのsituation calculus(situations/fluents)への評価と、PARCでそれを可能な範囲で実装しようとしたという回想はこの回答にある。ケイが同回答内で参照するマッカーシー自身の定式化は McCarthy, “Situations, Actions, and Causal Laws,” Stanford, 1963。時間を計算へ組み込むこの系譜(world-lines、Worlds等)は本稿の射程外とする。↩
-
前掲 McCarthy et al., LISP 1.5 Programmer’s Manual。
EXPR、FEXPR、SUBR、FSUBRの説明は、特に関数定義と評価器を扱う章を参照。↩ - Alan C. Kay, “The Early History of Smalltalk,” ACM SIGPLAN Notices, Vol. 28, No. 3, 1993, pp. 69–95; reprinted from History of Programming Languages II. HTML transcription / PDF(Appendix付き) / ACM DOI。特殊形式への批判(「宝石に残った傷」)と、「なぜ関数型言語と呼ぶのか。すべてをFEXPRに基づかせ、必要な評価を受け手側で強制してはどうか」という問いについては、同論文本文、1960年代の大学院時代にLISP 1.5と出会う場面の回想を参照。↩
-
前掲 Kay, “The Early History of Smalltalk”。
evalの分配、受信プロトコル、メッセンジャー、One Pagerの疑似コードは、同論文のAppendix II “Smalltalk Interpreter Design” を参照。本稿では、ユーザー提供の同付録の転記資料も照合に使用した。↩ - 前掲 Kay, “The Early History of Smalltalk”。PARCでの賭け、ケイの設計作業、インガルスによるBASIC実装については、本文の “The Birth of Smalltalk” 周辺を参照。↩
- Adele Goldberg and Alan Kay (eds.), SMALLTALK-72 Instruction Manual, SSL 76-6, Learning Research Group, Xerox Palo Alto Research Center, March 1976. Bitsavers PDF。本文の「検査機能」は、エラー発生時に評価の文脈を対話的に調べられる Diagnosis Window を指す(のちのSmalltalk環境のインスペクターにあたるものは、Smalltalk-72にはまだない)。同ウィンドウについては同マニュアルを参照。↩
-
前掲 Kay, “The Early History of Smalltalk”。
»(then)は、Appendix II “Smalltalk Interpreter Design” のFIGURE 11A.2に、to、ISNEW、=、.とともに掲げられている。用例は同論文本文中のSmalltalk-72コード例(class integerとして「内包的に」書かれたfactorialの定義や、“Proposed Smalltalk-72 Syntax” のPairクラスの例)にも見られる。↩ -
前掲 Kay, “The Early History of Smalltalk” の付録では
(REPLACA C (PLUS 3 4))と綴られているが、LISP 1.5での関数名はRPLACAである。↩ - Daniel H. H. Ingalls, “The Smalltalk-76 Programming System: Design and Implementation,” Proceedings of the 5th ACM SIGACT-SIGPLAN Symposium on Principles of Programming Languages, 1978, pp. 9–16. ACM DOI。Smalltalk-76の構造とバイトコード処理系については同論文を参照。Smalltalkの初期版からの変化については、前掲 Kay, “The Early History of Smalltalk” も参照。↩















