Lispとは何か
(人間の思考を計算機に渡す言語)

関連記事

1. LispのREPLはAIとのチャット

LispのREPLに向かってテキストを入力すると、計算機がすぐに評価して返します。
人間はその結果を見て次のテキストを考える。
アイデアを投げて、返ってきたものを受けて、また投げる。

中の仕組みはまったく異なりますが、構造的には、現在、生成AIのチャット画面でやっていることと似ています。
「人間がアイデアを言語化して投げ、システムが評価して返す」という往復の形には、Lispによって目指した「AI(人工知能)」を理解する手がかりになります。

1.1. Lispとは括弧の多い言語である

「Lisp」は、Fortranに次いで古い高水準プログラミング言語で1、数々の方言とその実装があります。

Lispの特徴は、

  • S式(Symbolic Expression)に基づく、括弧を使った前置記法の構文、
  • コンスセルを使った汎用的なデータ構造表現2
  • 自動メモリ管理であるガベージコレクション、

一つのプログラミング言語というより、共通の特徴を持った言語のグループで、今日広く使われているのはCommon Lisp や Scheme、Clojureですが、それ以外にも無数の方言が存在します。

2. Lispとプログラムの「常識」

Lispの開発の話は、1958年、ジョン・マッカーシーがMITに着任するところから始まります。

同年に書いた論文「Programs with Common Sense」のタイトルにあるように、「常識を持つプログラム」を作る、というのが Lisp の動機になっています3
また、1960年にACMの学会誌Communications of the ACMに発表した論文「Recursive Functions of Symbolic Expressions and Their Computation by Machine, Part I」が、Lispの理論的な基盤を作りました4

マッカーシーが立てた問いはこういうものでした。
人間は「机は家にある」「家は街にある」という知識を持ち、「机は街にある」と推論できる。
この推論を計算機にやらせるには、数値ではなく記号を操作する言語が必要だ、という着想です。

当時の計算機言語はFortranとCOBOLがありましたが、Fortranは科学的な数値計算のための言語で、COBOLは事務処理のための言語。
どちらも明確な実用的な用途がありました。

それに対して、Lispはそのような実用的な用途を念頭に置いて設計された言語ではありませんでした。
Lispの対象領域は、強いて言えば「記号処理そのもの」という抽象的なもの。
それゆえ、特定のドメインを持たず、AIにも数式処理にも言語処理にも使えました。

2.1. eval と ラムダ計算

Lispは、まず、自分自身を評価できる eval という関数を持った仮想的な言語として、設計されました。

その背景には、アロンゾ・チャーチのラムダ計算の理論があり、関数を値として扱い、関数が関数を返せるという数学的な枠組みが、そのままプログラミング言語に持ち込まれました5

そして論文の中で、Lisp自身でevalを書けることを示しました。
これは、「意味を解釈する機械を、その機械が話す言語で記述できる」という意味で画期的です。

2.2. S式のインタープリタ

しかし、当時、マッカーシー自身は、eval は理論的なもので実装できるとは思っていませんでした。
ところが、大学院生のスティーブ・ラッセルは、機械語で「S式のまま入力してS式のまま評価する」インタプリタを作り、論文にある eval を実装してみせました6

実は、マッカーシーは、プログラムの表現には S式でなく、M式という記法を使うつもりでした。

; M式のイメージ
; factorial[n] = if n=0 
                   then 1 
                   else n * factorial[n-1]

; S式のLisp
(defun factorial (n)
  (if (= n 0)
      1
      (* n (factorial (- n 1)))))Code language: Lisp (lisp)

M式は、car[x]f[x;y]のように書く、数学的な関数記法に近い形です。
「高級言語」とは、「人間が読むコードと計算機が扱うデータは別の形であるべきだ」という考え方によるものです。
S式はあくまでデータ用の内部表現であって、人間が直接書くものとは思われていなかったのです。

しかし、その後、マッカーシーが「実用ではない」と思っていたS式は、Lispそのものになります。
LISP I(1960年)のマニュアルにはM式の記法が残っていましたが、1964年の書籍「The Programming Language LISP: Its Operation and Applications」ではM式表記が完全に消えて、現代と同じS式だけになっています7

2.3. S式とグラフ構造的な思考

どうして、M式ではなく、S式が Lisp の記法になったのでしょうか。

普通のプログラミング言語では、人間に優しい文法を持ち、構文解析器が構文木に変換します。
一方、Lispは構文木をそのまま人間が書く。
まるで機械がやればいい作業を人間がやっているようなものです。
これは、機械語を読みやすくした、アセンブリやC言語などとは逆のアプローチに見えます。

S式はアセンブリとは別種の「計算機への近さ」を持っています。
アセンブリが近いのは「CPUが何をするか」という点ですが、S式が近いのは「インタプリタが何を読むか」という点。
リストとして表現されたコードは、インタプリタがそのまま読めるデータ構造になっています。
演算処理系ではなく、コード処理系に近い。

S式でプログラムを書くとき、「これを評価するとどうなるか」という視点に自然になっているのです。

3. MITのAI研究室

C言語にとっては、AT&Tベル研究所のUNIXが育つ環境でした。
Lispにとって、それはMITとXerox PARCのAI研究室だったと言えます。

1968年から1970年にかけてMITで開発された自然言語理解プログラムSHRDLUは、Lispで書かれた「AI」でした8
仮想空間に並んだ積み木に対して、「赤い積み木を青い箱の上に乗せて」という英語の命令を解釈して実行できるプログラムです。
「世界」を記号で表現し、その中での記号操作で「理解」を実現しようとした試みです。

Lispで書かれた数式処理システムMacsymaは、人間と同じような記号変換で微分や積分を解きました。
数値として計算するのではなく、x^2という記号を2xという記号に変換する。
これは、Lispが「記号を操作できる」という性質を直接使った応用です。

3.1. LISPマシンと対話環境

そして、「LISPマシン」があります。
Lispを動かすために専用設計されたハードウェアで、Xerox PARCやSymbolics社が1970〜80年代に作りました9
ガベージコレクションをハードウェアで支援し、タグ付きアーキテクチャで型チェックを高速化した専用コンピュータです。

同時期には、コモドール64やApple II といった「BASICマシン」もありました。
電源を入れるとBASICインタプリタが起動する。
まだ、コード実行系とOSの境界がなく、言語が環境そのものだった点は、LISPマシンと共通しています。

つまり、コンピュータマニアは、BASICでは計算機の仕組みそのものと格闘して、LISPでは抽象的な計算アイデアと格闘していたわけです。
このLISPマシンやBASICマシンのREPLでやっていたことは、今から見れば「コーディング」です。
しかし、当時の感覚は違ったかもしれません。

アイデアを式として入力する。
計算機が評価して返す。
その結果を受けて次のアイデアを考える。

コードで思考するコンピュータマニアにとっては、これらのREPLはすでにAIだったのだと思います。

多くのプログラミング言語では、コーディングと実行は分かれますが、Lispでは今でもREPLが重要な位置づけです。
「式を投げて評価を受け取る」というサイクルがLispの設計思想の中心にあります。

これは、ちょうど今の生成AIにとってチャットUIが「原点」であることと対応しているかのようです。

4. 記号とシンボル——意味を扱うとはどういうことか

Lispは、S式、つまりシンボルを扱うプログラミング言語です。

'APPLE        ; APPLEという名前そのもの(評価しない)
APPLE         ; 環境の中でAPPLEに束縛された値を返す
(setq APPLE '(red round fruit))
APPLE         ; => (RED ROUND FRUIT)Code language: Lisp (lisp)

Lispでの、APPLEというシンボルは、APPLEという名前そのものです。
シンボルは、評価されて初めて、環境の中での何かの意味や値に結びつきます10

4.1. 記号とは?

「記号」には、いろんな見方ができます。

記号が「意味をいかに持つか」「何かを何かの代理として表すとはどういうことか」を扱う学問は、記号論(semiotics)あるいは記号学(semiology)と呼ばれます11
また、「形式体系の部品」として記号をとらえる、数理論理学や形式言語論もあります。

たとえば、ソシュールの記号論では、記号は音や形と意味が一体になったものです12
「りんご」という語は、音の連鎖と「赤くて丸い果物」という概念が対になって初めて記号になる、という考え方です。

あるいは、記号論理学では別の見方をします。
P → Qという式のPに何が入るかは問いません。
形式的な変換規則だけが重要で、記号は意味の担い手ではありません。

Lispのシンボルは、記号の持つどちらの要素もあります。
quoteはシンボルを「名前として扱え」という指示で、evalは「評価せよ」という指示です。
シンボルは、変数として値に対応づけられたり、関数定義に結びついたりするものの、クオートされればシンボル自身がデータにもなる。

Lispのシンボルは、「評価と束縛によって意味機能を得る記号」といえます。

4.2. 記号処理AI

コードがシンボルのリストであることは、人工的に「知能」を設計する上で重要だと考えられました。

LispのAIの推論エンジンでは、知識やルールをリストとして表現して、プログラムがそれを読んで評価しました。
たとえば、SHRDLUのような記号処理AIは、この構造を直接使っていました。

;; 知識ベース:事実をリストとして持つ
(defvar *facts*
  '((ON BLOCK-A TABLE)
    (ON BLOCK-B BLOCK-A)
    (COLOR BLOCK-A RED)
    (COLOR BLOCK-B BLUE)))

;; 推論ルールもリストとして持つ
(defvar *rules*
  '(((ABOVE ?x ?y) (ON ?x ?y))
    ((ABOVE ?x ?z) (ON ?x ?y) (ABOVE ?y ?z))))

;; 事実を問い合わせる
(defun query (pattern facts)
  (find-if (lambda (fact)
             (match pattern fact))
           facts))Code language: Lisp (lisp)

ルール自体がLispのリストなので、プログラムがルールを読んだり、あるいは書き換えて、追加できます。
推論エンジンがルールを「データとして操作」しながら「コードとして評価」する。
この二つが同じ形だから成立します。

たとえば、Javaのようなプログラミング言語でルールエンジンを作ろうとすると、クラス、インターフェース、ファクトリーと大がかりな設計構造が必要になります。
しかし、Lispには柔軟性があるので、パターンを実装する前に解決したわけです。
ピーター・ノーヴィグはGoFのデザインパターン23種のうち16種はLispでは言語機能で直接実現できると指摘しています13

5. シンボルと意味の束縛性

しかし、Lispとシンボルと自然言語の記号には、決定的な違いがあります。

LispのAPPLEは、ある時点で一つの値にしか束縛できません。
言語記号の多義性を同時に保持できません。
意味がシンボルの内部にあるのではなく環境と文脈の関係で成立するにもかかわらず、その環境は一時点で一つの値しか与えられない。

一方、人間が自然言語で「りんご」を評価するとき、腹が減っているか、季節は何か、誰と話しているかで「りんご」の重みが変わります。
文脈が評価に参加していて、概念の輪郭が毎回作り直される。
青りんごはりんごか、腐りかけはりんごか、という問いにも明確な答えがありません。
自然言語の語は共同体の中で比較的安定した意味を先に持っていて、文脈によってその意味が変幻自在に動く。

これが、後に記号処理AIにとって、大きな壁になります。

5.1. 記号処理の限界と「AIの冬」

SHRDLUは「積み木の世界」から出られませんでした。

積み木の世界では、存在するオブジェクトの種類も、起きうる出来事も、すべて事前に記号として定義できます。
だから記号の操作で「理解」が成立した。
しかし、現実世界に出ようとした瞬間に、世界を記号で記述する作業が無限に続くことがわかりました。

これが「AIの冬」につながります。
1980年代後半、記号処理AIへの投資が冷えました14

「犬は動物だ」「動物は生き物だ」「生き物は死ぬ」。
この連鎖を書き続けてもキリがない。
常識の量が膨大すぎたのです。

この問題の根本は、シンボルが文脈の多義性を同時に保持できないことです。

5.2. LLMとトークン

後に、「生成AI」と呼ばれる大規模言語モデル(LLM)は別の方向から迂回しました。

意味を記号で定義しようとせず、大量のテキストからトークンの共起パターンを学習する。
意味を記号の構造ではなく、ベクトル空間の中の位置として表現する。

記号からトークンへの移行は、意味の扱い方そのものの転換でした。
「りんご」の多義性は、文脈に応じてベクトルが変化することで表現されます。

記号処理AIとLLMは、「意味をどのように扱うか」という問いに対して、対照的な答えを出したのです。

6. Common LispとSchemeへの分岐

とはいえ、「AIの冬」の中で、Lispは大きな転換点を迎えました。
それは、「プログラミング言語」としての道です。

Lispが「AIそのもの」だった時代は、各AI研究室が自分のアイデアを評価するために独自のLispを作り上げていました。
語彙の拡張によって思考ツールをカスタマイズするのが、Lispの本来の使い方でした。

しかし、AIの冬で状況が変わると、「AIの設計ツール」から「ソフトウェアを作る言語」へと重心が移ります。
Lispは、ソフトウェアを記述するのにも有効な言語でしたが、そこで方言の乱立が問題として浮上しました。
研究室の外でも使われるためには互換性や可搬性、つまり同じコードがどこでも同じように動くことが必要だったのです。

サスマンとガイ・スティール・ジュニアは、1975年に「小さなLisp」としてSchemeを作りました15
Schemeは思想を純化した小さな仕様で、仕様書が50ページにも満たないのが特徴です16

一方、1970年代にMACLisp系とInterlisp系を中心に30以上あった方言を統合しようとして、1984年にCommon Lispが設計されます。
Common Lispは、実用重視で、オブジェクトシステムであるCLOS、複素数、多倍長整数まで取り込んだ大きな仕様です。

この対照的な 2つの Lisp が、その後幅広く使われるようになるのですが、Schemeの設計者であるガイ・スティール・ジュニアが、Common Lisp標準化委員会の議長でもあったことは興味深いです17

6.1. Lispが渡したもの、変わらなかったもの

Lispそのもの以上に、この言語の様々な要素は、現代のプログラミング言語に影響を残しています。
PythonもRubyもJavaScriptにも、Lispが生み出した設計が引き継がれています。

再帰、レキシカルスコープとクロージャ、高階関数、無名関数、ガベージコレクション、動的型付け、REPLによるインタラクティブ開発。
今では当たり前に使われているものばかりですが、これらは1960年代当時の主流言語には全くなかった概念で、Lispが先に実装し、後の言語が引き継ぎました18

; Schemeで完成したクロージャ
(define (make-counter)
  (let ((count 0))
    (lambda ()
      (set! count (+ count 1))
      count)))

(define c (make-counter))
(c)  ; => 1
(c)  ; => 2
(c)  ; => 3Code language: Lisp (lisp)

6.2. 言語が話せるソフトウェア

アプリケーションソフトの中にもLispは残っています。
エディタEmacsやブラウザNyxtは、LISPマシンの系譜の生き残りといえるかもしれません。

VSCodeやChromeは、GUIを人間が操作する道具です。
しかし、EmacsやNyxtはLispで書くことでアプリの内部のAPIが応答します。

今は、多くのアプリに自然言語によるAI拡張UIが追加されていますが、この原型が Emacs にはあります。

生成AIがLispのコードを書けるようになった今、この二つが近づいています。
自然言語で話しかけてLLMがLispのコードを生成して、EmacsやNyxtのAPIに直接アクセスする、という流れが実際に起きています。

6.3. ニューロシンボリックAI

そして、より大きな可能性があります。

記号処理AIが詰んだのはルールを人間が書き続けなければならなかったからです。
一方、LLMはその制約を持たないけれど、決定論的な検証ができない。
なぜ、そう答えたか説明できず、ハルシネーションを止められない。

LLMが統計的にルール候補を大量生成して、Lisp的な記号処理系が決定論的に検証して矛盾や不要なものをそぎ落とす。
人間がルールを書く部分をLLMが担い、検証と実行は記号処理系が担う。

ニューロシンボリックAIと呼ばれるこの方向は、1958年に記号処理AIが目指したものとLLMの生成力を組み合わせる試みです19

変わったのは、解釈する側の中身だけではありませんでした。
Lispが1958年に目指したものの輪郭が、逆説的に今くっきり見えてきます。

  1. LISPはFORTRANに次いで2番目に古い高水準プログラミング言語です。FORTRANが1957年、LISPが1958年の誕生です。 – Lisp (programming language) – Wikipedia
  2. コンスセルはcarとcdrという二つのスロットを持つデータ構造で、リストはコンスセルを繋ぎ合わせて表現します。最後のコンスセルのcdrだけはnilを参照します。 – Common Lisp – Wikipedia
  3. 「Programs with Common Sense」は1958年12月のTeddington Conference on the Mechanization of Thought Processesで発表され、1959年に会議録として出版されました。記号論理学をAIの知識表現に用いた最初の論文とされています。 – Programs with Common Sense – jmc.stanford.edu
  4. この論文はLISPの設計を正式に発表したもので、S式、ガベージコレクション、evalという概念を定義しました。「Part II」が発表されることはありませんでした。 – Recursive Functions of Symbolic Expressions – www-formal.stanford.edu
  5. アロンゾ・チャーチ(1903-1995)は数学者・論理学者で、1930年代にラムダ計算を考案しました。ラムダ計算は計算可能性理論の基礎となり、関数型プログラミング言語の理論的な土台になっています。 – Lambda calculus – Wikipedia
  6. マッカーシーはラッセルに対して「君は理論と実践を混同している。このevalは読むためのもので、計算するためのものではない」と言いましたが、ラッセルは実装してみせました。マッカーシーは後にこの出来事を回顧しています。 – John McCarthy and the origin of Lisp – johndcook.com
  7. M式はMLisp(Horace Enea)やCGOL(Vaughan Pratt)での短命な試みで再登場しましたが、どちらも普及しませんでした。今日ほぼすべてのLisp処理系がS式だけを使っています。 – Lisp (programming language) – Wikipedia
  8. SHRDLUはMicro PlannerとLispで実装され、DEC PDP-6コンピュータ上で動作しました。世界をわずか50語(block、coneなどの名詞、place on、move toなどの動詞)で記述できるほど単純化した点が、成功と限界の両方の理由でした。 – SHRDLU – Wikipedia
  9. Symbolicsは1980年にMIT AIラボのスピンオフとして創業し、3600シリーズなどのLISPマシンを製造しました。OSから開発環境まで約50万行がすべてLispで書かれていました。1996年に破産しました。 – Symbolics – Wikipedia
  10. パース(Charles Sanders Peirce, 1839-1914)の記号論では、記号(representamen)は対象(object)と解釈素(interpretant)の三項関係で成立します。Lispのシンボルはこのうち「何かを指しうる媒体」としての表象体に近く、それが何を指すかは評価規則と環境によって決まります。 – Peirce’s Theory of Signs – Stanford Encyclopedia of Philosophy
  11. 記号論(semiotics)はチャールズ・サンダース・パースが体系化し、記号学(semiology)はフェルディナン・ド・ソシュールが提唱しました。両者は出発点が異なりますが、現代では広く「記号の科学」として統合的に扱われています。 – Semiotics – Stanford Encyclopedia of Philosophy
  12. フェルディナン・ド・ソシュール(1857-1913)はスイスの言語学者で、記号をシニフィアン(音・形)とシニフィエ(意味・概念)の対として定義しました。この考え方は20世紀の記号論の基盤となりました。 – Saussure – Stanford Encyclopedia of Philosophy
  13. ノーヴィグは1996年のObject World conferenceでの発表「Design Patterns in Dynamic Languages」でこの分析を示しました。正確には「16 of 23 patterns have qualitatively simpler implementation in Lisp or Dylan than in C++ for at least some uses of each pattern」と述べています。 – Design Patterns in Dynamic Languages – norvig.com
  14. 1987年に特化型AIハードウェア市場が突然崩壊したことが、AIの冬の最初のシグナルとされています。SHRDLUの成功が過度な楽観論を生み、その後の失敗への反動が投資の急速な縮小につながりました。 – History of artificial intelligence – Wikipedia
  15. Schemeはカール・ヒューイットのアクター言語Plasmaを理解するために設計されました。設計当初の名前はSchemerでしたが、当時のオペレーティングシステムのファイル名6文字制限によってSchemeに短縮されました。 – Scheme – Wikipedia
  16. R5RSはRevised5 Report on the Algorithmic Language Schemeの略称で、1998年に策定されました。その後R6RS(2007年)、R7RS(2013年)と改訂が続いています。 – Scheme – Wikipedia
  17. ガイ・スティール・ジュニアはジェラルド・サスマンとともにSchemeを設計し、後にCommon Lisp標準化委員会の議長を務めました。対照的な二つの設計思想に同じ人物が関わったことは、Lisp史の中で際立った逸話です。 – Common Lisp – Wikipedia
  18. Lispが先駆けた概念として、Wikipedia英語版は木構造、自動メモリ管理、動的型付け、条件分岐、高階関数、再帰、セルフホスティングコンパイラ、REPLを挙げています。 – Lisp (programming language) – Wikipedia
  19. ニューロシンボリックAIは記号処理とニューラルネットワークを組み合わせるアプローチで、LLMの生成力と記号処理の決定論的な検証を組み合わせることで、それぞれの弱点を補う研究が進んでいます。 – History of artificial intelligence – Wikipedia