ジョン・マッカーシーの記号的アプローチとLispの設計(リスト、再帰、ガベージコレクション)がAIと現代のプログラミングにどう影響したかを探る。

これは「古いAIの博物館ツアー」ではありません。プログラマー、テックリード、プロダクト作りに関わる人にとっての実践的な歴史です。ジョン・マッカーシーの考えは、プログラミング言語が何のためにあるかという見方を形作りました。
Lispは単なる新しい構文ではありませんでした。ソフトウェアがアイデア(単なる数値ではなく)を操作できるという賭けであり、言語設計の選択が研究、プロダクトの反復、ツールエコシステムの発展を加速できると考えたものです。
マッカーシーの遺産を読む有用な視点は、今日でも重要な問いとして捉えることです:どれだけ直接的に意図を実行可能なシステムに変換できるか—ボイラープレートや摩擦、偶発的複雑さに溺れずに? その問いはLispのREPLから現代の「チャット→アプリ」ワークフローまで響いています。
マッカーシーはAIを研究分野として立ち上げたことだけでなく、アイデアを操作できる特定のタイプのAIを主張したことで記憶されています。1950年代半ばに彼はダートマス夏季研究プロジェクトを組織し(そこで「人工知能」という用語が提案されました)、MITや後のスタンフォードでAI研究に影響を与えました。しかし最も持続的な貢献は彼が常に問い続けたことかもしれません:推論自体をプログラムとして表現できたらどうなるか?
初期の計算成功の多くは数値的なものでした:弾道表、工学シミュレーション、最適化、統計など。これらは算術にうまく収まります。
マッカーシーが目指したのは別種のものです。人間の推論はしばしば「もし〜なら」「なぜなら」「〜に属する」「〜の一種だ」「これらの条件を満たすすべてのもの」といった概念で動きます。これらは浮動小数点値として自然に表現されるものではありません。
マッカーシーのアプローチは知識を記号(名前、関係、カテゴリ)として扱い、思考をそれらの記号に対する規則的変換として扱いました。
高レベルで言えば:数値的アプローチは「どれだけ?」に答えるのに対し、記号的アプローチは「それは何か?」と「知っていることから何が導かれるか?」に答えようとします。
推論をプログラム可能にできると信じたら、ルールや論理式、入れ子になった関係を快適に表現し、処理できる言語が必要です。
Lispはその目的のために作られました。アイデアを堅い事前定義のデータ構造に無理に押し込む代わりに、Lispはコードと知識を似た形で表現することを自然にしました。この選択は学問的な流儀ではなく、思考を記述することと手続きとして実行することの間の実用的な橋渡しでした。マッカーシーがAIに求めたのはまさにこの橋です。
マッカーシーや初期のAI研究者が「記号的」と言ったとき、難解な数学を指していたわけではありません。記号とは単に意味のあるラベルのことです:customer のような名前、hungry のような単語、IF や THEN のようなタグ。記号はプログラムがアイデア(カテゴリ、関係、ルール)を数値だけで扱うのではなく直接扱えるようにします。
分かりやすい例え:スプレッドシートは列と算術が中心の世界に適しています。記号システムはルール、カテゴリ、例外、構造が中心の世界に適しています。
多くのプログラムでは、42 と "age" の違いはデータ型だけでなく、その値が何を表すかです。記号は意味を失わずに比較、保存、結合できます。
そのため「Paris は都市である」や「バッテリーが少ない場合は充電器を探す」といったことを自然に表現できます。
記号で有用なことをするには構造が必要です。Lispは非常に素朴な構造、リストを普及させました。リストは順序づけられた項目の集合で、その項目自身がリストになり得ます。この一つの考えで文章、フォーム、木構造の知識を表現できます。
ここに小さな概念例(Lisp風)を示します:
(sentence (subject robot) (verb needs) (object power))
英語のように読めます:主語、動詞、目的語からなる文章。構造化されているため、プログラムは (subject robot) を取り出したり (object power) を別のものに置き換えたりできます。
情報が記号構造に入ると、古典的なAIタスクが実現しやすくなります:
重要なのは、プログラムが単に計算しているのではなく、意味のある知識の断片を検査・変換していることです。
Lispの設計決定は学界の内側にとどまりませんでした。人々がツールをどう作るか、アイデアをどれだけ迅速に試せるかに影響しました:
これらの特徴は実験が安くなり、プロトタイプが製品へと速く移り、要件が変わってもチームが適応しやすくなります。
Lispは非常に実践的な設計問題から始まりました:記号を数値と同じように自然に扱える言語はどう作るか?
マッカーシーは「より良い電卓」を作ろうとしたわけではありません。彼が望んだのは (is (parent Alice Bob)) のような式を (+ 2 3) と同じくらい簡単に保存し、検査し、変換し、推論できる言語でした。
優先されたのは記号情報を表現・操作しやすくすることでした。そのためリストや木構造に焦点を当てました。これらは人間が意味を表すために使う既存の形式(文章、論理規則、入れ子のカテゴリ、関係)に良く対応します。
もう一つの目標はコアを小さく一貫させることでした。言語に特別扱いが少ないほど、覚えるルールが減り、別のアイデアを組み合わせることに時間を使えます。Lispは少数の基本要素を組み合わせてより大きな抽象を作る方向に傾きました。
重要な洞察はプログラムとデータが同じ構造を共有できるという点です。要するに:データがネストしたリストなら、プログラムもネストしたリストにできます。
それにより次が可能になります:
Lispはまた、言語は一つの万能型でなくてもよいというマインドセットを普及させました。推論、探索、知識表現のような問題領域に合わせて設計された言語が、数十年にわたり汎用的なプログラミングにも影響を与えうることを示しました。
S式(symbolic expressions)はLispの象徴的な考え方で、コードとデータをネストしたリストとして一貫して表現します。
一見すると、S式は括弧で囲まれた項目の集合に過ぎません—項目の一部はアトム(名前や数値)で、他はさらにリストです。「リストの中にリストがある」ルールが要点です。
構造が均一であるため、Lispプログラムは下から上まで同じ基本要素で構成されます。関数呼び出し、設定のようなデータ、プログラムの一部構造のどれもリストで表現できます。
この一貫性には即座に利点があります:
たとえLispを書かないとしても、この設計教訓は重要です:システムが一つか二つの予測可能な形から構築されていれば、端っこの問題と格闘する時間が減り、機能構築に集中できます。
S式は小さく読みやすい部品が自然に大きな部品に結合されることを促します。プログラムが「ただのネストしたリスト」であるとき、アイデアの組み合わせはしばしば式を入れ子にすることか、再利用可能な部品からリストを組み立てることに等しいです。
これによりモジュール化が促されます:小さな操作を一つだけ行うように書き、それらを重ねてより大きな意図を表現します。
明らかな欠点は慣れの必要性です。多くの新人にとって括弧多めの構文は奇妙に見えます。
しかし利点は予測可能性にあります:入れ子ルールを一度理解すれば、プログラムの構造を確実に把握でき、ツールも同様に扱えます。この明瞭さがS式がLispを超えて影響を与えた大きな理由です。
再帰は日常的な比喩で理解しやすい:散らかった部屋を小さな「部屋」に分けて片付けるようなものです。一度にすべてを解決しようとせず、1つのアイテムを片付けてから残りに同じ処理を繰り返します。ステップは簡単で、繰り返すことで力を発揮します。
Lispは多くのデータがリストで構成されるため、この考え方に依存します:リストには「最初の要素」と「残り」がある。その形は再帰的思考に非常によく合います。
リストを処理するには、最初の要素を処理し、同じロジックを残りに適用します。リストが空になったら止めます—これが「やることが何も残らない」という明確な終端で、再帰を神秘的ではなく定義されたものにします。
数値のリストの合計を求めるとします。
これだけです。定義は英語のように読みやすく、プログラム構造がその考えを反映します。
記号的AIはしばしば式を木構造(演算子と部分式)として表現します。再帰はその木を「歩く」自然な方法です:左側を評価するのと同じ方法で右側を評価し、単純な値に到達するまで続けます。
これらのパターンは後の関数型プログラミングに影響を与えました:小さな関数、明確な基本ケース、副作用の少ないデータ変換。Lisp以外でも「一つのステップを行い、残りに繰り返す」習慣は、よりきれいでバグの少ないコードにつながります。
初期のプログラマは手動でメモリを管理する必要がありました:領域を確保し、誰が所有しているかを追跡し、適切なタイミングで解放することを忘れないようにする。この作業は開発を遅らせるだけでなく、再現しづらく致命的になりうるバグ(リーク、ダングリングポインタ)を生みます。
ジョン・マッカーシーはLispのためにガベージコレクションを導入し、プログラマが簿記作業ではなく意味に集中できるようにしました。
高レベルでは、GCは実行中のプログラムから到達不能になったメモリ(今後何も参照しない値)を自動で見つけ出して回収します。
「各オブジェクトをちょうど一度解放したか?」と問い続ける代わりに、GCでは「このオブジェクトはまだ到達可能か?」という問いに置き換わります。プログラムから到達できなければゴミと見なされます。
記号的AIの作業では、Lispプログラムは短命のリストやツリー、中間結果を頻繁に作ります。手動メモリ管理だと実験がリソースのクリーンアップとの絶え間ない闘いになってしまいます。
GCは日々の体験を変えます:
言語機能はチームの乗数になり得るというのが核心です:謎めいた破損をデバッグする時間が減れば、その分ロジック改善に時間を使えます。
マッカーシーの選択はLisp内に留まりませんでした。後の多くのシステムがGC(その変種)を採用しました。なぜならそのトレードオフはしばしば報われるからです:Java、C#、Python、JavaScriptのランタイム、Goなどは大規模開発を安全かつ高速にするためにGCに依存しています。
Lispでは式は一貫した形(多くはリスト)で書かれるコード片です。評価はその式が何を意味し、何を生成するかを決めるプロセスです。
たとえば「これらの数を足す」や「この関数をこれらの入力で呼ぶ」と書くと、評価器は一連のルールに従ってその式を結果に変換します。言語の審判者のようなものだと考えてください:次に何をするか、どの順序で、いつ止めるかを決めます。
マッカーシーの重要な一手は新しい構文を発明することだけでなく、「意味エンジン」を小さく規則的に保ったことです。評価器がいくつかの明確なルールで作られていると次の二つが起きます:
この一貫性がLispを記号的AIのアイデア実験場にした理由の一つです:研究者は新しい表現や制御構造を素早く試せ、コンパイラチームの待ち時間を必要としませんでした。
マクロは単に値の繰り返しを避けるのではなく、繰り返されるコード形状を自動化します。関数が計算の重複を避けるのに対し、マクロは「Xを行うが同時にログも取る」「ルール用のミニ言語を定義する」といった構造的な反復を取り除きます。
実務的効果は、Lispが内部から新しい便利機能を育てられることです。現代の多くのツールもこの考えを反映しています—テンプレートシステム、コードジェネレータ、メタプログラミング機能はいずれも高速な実験と明確な意図を支援します。
このマインドセットが日常の開発フローにどう影響したか興味があれば、/blog/the-repl-and-fast-feedback-loops を参照してください。
Lispの魅力は言語そのものだけでなく、使い方にもありました。LispはREPL(Read–Eval–Print Loop)を普及させました。日常的には、コンピュータと会話しているようなものです。式を打ち込むと、システムが即座にそれを実行し、結果を表示して次の入力を待ちます。
プログラム全体を書いて、コンパイルして、実行して、壊れた箇所を探す代わりに、小さなステップでアイデアを試せます。関数を定義して、少数の入力で試し、調整してまた試す—すべて数秒でできます。
このリズムは実験を促進します。初期のAI作業では正しいアプローチが分からないことが多く、迅速な試行が非常に重要でした。
短いフィードバックは「大きな賭け」を「小さな検証」に変えます。研究では仮説を探索し中間結果を検査しやすくなります。
プロダクトのプロトタイピングでは反復コストが減る:実データで挙動を素早く検証し、端ケースに早く気づき、長いビルドサイクルを待たずに改良できます。
これは現代のvibe-codingツールが魅力的なのと同じ原理です。たとえばKoder.aiはチャットインターフェース(内部ではエージェントベースのアーキテクチャ)を使ってプロダクトの意図を素早くWeb、バックエンド、モバイルコードに変換し、試す→調整→再試行のループをREPLに近づけます。
REPLの考え方は今日次のような場所に現れています:
ツールは違えど原理は同じ:思考と視覚化の距離を短くする。
不確実な要件を探索しているとき、データ重視の機能を作るとき、APIを設計するとき、複雑なロジックをデバッグするときにチームはREPL的ワークフローから大きな恩恵を受けます。学習が多い作業—ユーザー、データ、端ケースについて学ぶ作業—では対話的フィードバックは贅沢ではなく乗数要因です。
Lispは文法で勝利したわけではありませんが、アイデアを撒き散らし、それが多くのエコシステムで標準になりました。
Lispが当然とした概念—関数を値として扱うこと、高階関数の多用、小さな部品の合成—は今や広く見られます。Lispらしくない言語であっても map/filter 型の変換、不変データの習慣、イテレータやfoldで表現される再帰的思考が奨励されています。
重要なのは心構えの変化です:データ変換をパイプラインとして捉え、振る舞いを渡せるものとして扱う。
Lispはプログラムをデータとして表現しやすくしました。この考え方はコンパイラやフォーマッタ、リンタ、コードジェネレータでAST(抽象構文木)を扱う方法に現れます。ASTを扱うということは、たとえ構造がJSONオブジェクトや型付きノード、バイトコードグラフであっても「コードをデータとして扱う」近い親戚を扱っていることになります。
同じ記号的アプローチが実用的な自動化を支えます:設定フォーマット、テンプレートシステム、ビルドパイプラインはどれもツールが検査、変換、検証できる構造化表現に依存しています。
現代のLisp系言語(広義の意味で:現代のLispやLispに影響を受けたツール)は、テスト、デプロイ、データ処理、UIのための小さな内部DSLの設計に影響を与え続けています。
Lisp以外でも、マクロシステム、メタプログラミングライブラリ、コード生成フレームワークは同じ結果を目指します:問題に合わせて言語を拡張すること。
実用的な示唆としては、構文の好みは変わるが、耐久的なアイデア—記号的構造、合成可能な関数、拡張性—は何十年にもわたり価値を生み続けるということです。
Lispは「素晴らしい」から「読みづらい」まで評価が分かれますが、多くは伝聞に基づく印象です。実際はもっと平凡です:Lispは適切な状況で強力で、そうでない場面では不便になります。
慣れない人にはLispの均一な構文がプログラムの「内部」を見ているように感じられます。これは、構文が異なる要素を視覚的に分ける言語に慣れているときに起こる不快感で、確かに実在します。
しかし歴史的には、構造が明確であること自体が目的でした:コードとデータが同じ形をしていることで、プログラムの生成、解析、変換が容易になります。適切なエディタサポート(インデント、構造的ナビゲーション)があれば、Lispコードは括弧を数えるのではなく形で読まれます。
「Lispは遅い」という通説があります。歴史的には一部の実装が低レイヤの言語に比べて苦戦したことは確かで、動的機能はオーバーヘッドを生みます。
しかし「Lisp」という単一の性能プロファイルがあるわけではありません。多くのLisp実装は昔からコンパイルや型注釈、最適化をサポートしてきました。重要なのは、どれだけメモリ配置、予測可能なレイテンシ、純粋なスループットの制御が必要か、そして使う実装がそのニーズに合っているかを評価することです。
別の現実的な批判はエコシステムの適合です。Lispの方言や対象ドメインによっては、ライブラリ、ツール、採用可能な人材がメインストリームのスタックより小さいことがあります。製品を迅速に出荷するチームにとってはこれは言語の良さより重要になることがあります。
ステレオタイプでLispを判断するのではなく、均一な構造、対話的開発、ドメイン固有抽象化を作るためのマクロという道具立てといった基盤的なアイデアを個別に評価してください。Lispを直接使わなくても、これらの概念は言語設計や日常のコーディングを研ぎ澄ます助けになります。
マッカーシーが残したのは歴史的な言語だけではなく、ソフトウェアを変更しやすく、説明しやすく、拡張しやすくする習慣です。
巧妙な表面より単純なコアを優先する。 直交する小さな基本要素は学びやすく壊れにくい。
データ形を均一に保つ。 多くのものが同じ表現を共有すれば、プリンタ、デバッガ、シリアライザ、トランスフォーマを再利用できる。
必要ならプログラムをデータとして扱う(データをプログラムとして扱う)。 構造を検査・変換できれば、安全なリファクタリング、移行、コード生成が可能になる。
面倒な作業を自動化する。 ガベージコレクションは古典例だが、広く言えばミスのクラスを防ぐ自動化に投資すること。
フィードバックループを最適化する。 対話的評価(REPLスタイル)は小さな実験と迅速な検証を促す。
拡張を第一級の設計目標にする。 Lispのマクロは一つの解だが、他のエコシステムではプラグイン、テンプレート、DSL、コンパイル時変換がそれに当たる。
ライブラリやアーキテクチャを選ぶ前に、実際の機能(例:「割引ルール」や「サポートチケットのルーティング」)を取り、それを木として描いてください。次にその木をネストしたリスト(あるいはJSON)として書き、ノードと葉、必要な変換を考えるとよいでしょう。
Lispがなくても次のマインドセットを採用できます:
多くのチームは中間表現を明示化するだけで恩恵を得ます。
REPLの原則が好きだが主流スタックでプロダクト機能を出す場合は、ツールで精神を借りることもできます:高速な反復ループ、スナップショット/ロールバック、実行前の明示的プランニングなど。Koder.aiは例えばプランニングモードやスナップショット、ロールバックを備え、迅速な反復を安全にするという点でLispの「速く変えるが制御を保つ」という哲学の運用的な反響を示しています。
マッカーシーの持続的な影響はこれです:推論そのものをプログラム可能にし、アイデアから実行可能なシステムへの道筋をできるだけ直接に保つほど、プログラミングは強力になる。
記号的思考は、概念や関係を直接表現し(例:「customer」「is-a」「depends-on」「if…then…」)、それらの表現に対してルールや変換を適用する考え方です。
構造や例外、意味が多く含まれる問題(ルールエンジン、プランニング、コンパイラ、設定、ワークフローのロジックなど)で特に有用です。
マッカーシーは「推論はプログラムとして表現できる」という考えを推し進めました。単なる計算ではなく、推論そのものをプログラムで扱えるようにするという視点です。
この考えは次の点に影響を与えました。
リストは「部品からできているもの」を表現するための、最小で柔軟な方法です。リストの要素はさらにリストになり得るので、自然に木構造が得られます。
これにより次のことが簡単になります。
subject ノード)S式(symbolic expressions)はコードとデータをネストしたリストとして一様に表現する方法です。
その一貫性により、システムは単純になります。
この単一形状の考え方が設計上大きな利得をもたらします。
マクロは、繰り返されるコード構造を自動化するための仕組みです。関数が計算の再利用を助けるのに対して、マクロはコードの形そのものを再利用可能にします。
マクロを使うと次が可能です。
もし単に再利用できるロジックが必要なだけなら、通常は関数の方が適切です。
ガベージコレクション(GC)は、到達不能になったメモリを自動で回収する仕組みです。これにより次のようなバグのクラスを減らせます(ダングリングポインタ、二重解放など)。
特にリストやツリー、ASTなど短命な構造を大量に作る場面では、GCがあることでプロトタイピングやリファクタリングが格段に楽になります。
結果として、開発生産性の向上(デバッグ時間の削減、設計に集中できる)につながります。
REPLは「読む–評価–表示–待ち」のループで、コンピュータと会話するように小さな式を逐次試せる仕組みです。関数を定義してすぐにテストし、修正して再実行できるため、思考→試行→観察のループが短くなります。
非Lispスタックでも同様の恩恵を得るためには:
関連読物: /blog/the-repl-and-fast-feedback-loops
Lispは構文そのものでは多くの人の標準にならなかったものの、そのアイデアは多くのエコシステムに浸透しました。
map/filterのような関数型パターンたとえLispを直接使わなくても、Lisp由来の習慣は日常的に使われています。
現実的なトレードオフは次の通りです。
結論としては、評判で判断するよりも、ドメインと制約に照らして適合性を評価するのが実用的です。
10分ワークの例:
この演習で「コードをデータとして扱う」パターンや、ルールエンジン、DSLの導入が有効かどうかが見えてきます。