1 歴史と標準化
1.1 Lispの起源とCommon Lispの誕生
Lisp(LISt Processing)は、1958年にジョン・マッカーシーによって開発された、世界で2番目に古い高水準プログラミング言語である。その後、多数の方言(MacLisp、Interlisp、ZetaLispなど)が生まれ、1980年代初頭にはその断片化が問題となった。これを受けて、主要なLispコミュニティの研究者や企業が集結し、統一された方言としてCommon Lispの設計が開始された。1984年に、ガイ・スティール・ジュニアの著書『Common Lisp the Language』(通称CLtL1)が事実上の仕様書として発表され、Common Lispが誕生した。
1.2 ANSI標準化プロセス
1986年から、American National Standards Institute(ANSI)の下でX3J13委員会がCommon Lispの正式な標準化を進めた。委員会には、シンボリックス、ルーセント、フランツ、アップルなどの企業や、多くの個人研究者が参加した。長い議論と改良の末、1994年にANSI X3.226-1994として標準規格が承認された。この標準は、CLOS(Common Lisp Object System)の追加、ループマクロの導入、条件システムの洗練など、多くの重要な拡張を含む。
1.3 その後の発展と影響
ANSI標準化後、Common Lispは商業アプリケーションや人工知能研究の基盤として広く利用されたが、1990年代後半にはJavaやC++の台頭により人気が低下した。しかし、2000年代以降、SBCL(Steel Bank Common Lisp)やClozure CLなどの高性能なオープンソース実装の登場、Quicklispによるパッケージ管理の革新、さらにはマクロシステムの強力さへの再評価により、コミュニティは再活性化した。現在も、Web開発、データサイエンス、ゲーム開発など多様な分野で使用されている。
2 言語の基本要素
2.1 データ型
2.1.1 数値型(整数、浮動小数点数、有理数)
Common Lispは豊富な数値型を提供する。整数は任意精度(bignum)をサポートし、符号付き・符号なしの両方に対応する。浮動小数点数は単精度(short-float)、倍精度(double-float)、および拡張精度(long-float)を含む。有理数(rational)は、分数として正確に表現され、整数と浮動小数点数の間の演算時に自動的に使用される。複素数(complex)も組み込み型として存在する。
2.1.2 コンスセルとリスト
コンスセル(consセル)は、Lispの基本データ構造であり、carとcdrという2つのフィールドから構成される。コンスセルをチェーン状に連結することで、リストが形成される。空リストはnil(同時に偽値)として表現される。リスト操作関数として、car、cdr、cons、append、reverseなどが提供される。ドット対(ドットリスト)もサポートされ、ドット記法で表現される。
2.1.3 シンボルとパッケージ
シンボルは、名前を持つ一意のオブジェクトであり、通常変数や関数の名前として使用される。各シンボルは、値(value cell)、関数定義(function cell)、プロパティリスト(property list)を持つ。パッケージはシンボルの名前空間を管理する仕組みであり、シンボルのエクスポート、インポート、継承を制御する。in-packageやdefpackageを使用してユーザー定義パッケージを作成できる。
2.1.4 配列、ハッシュテーブル、文字列
配列は固定長で多次元のデータ構造であり、make-arrayで作成される。文字列は文字の順序付き配列として実装され、string型として扱われる。ハッシュテーブルはキーと値のマッピングを提供し、make-hash-tableで作成し、gethashでアクセスする。これらはすべて実行時に動的に操作可能である。
2.2 制御構造
2.2.1 条件分岐とループ
条件分岐には、if(基本的な分岐)、cond(多分岐)、whenとunless(条件付き実行)が使用される。ループは、伝統的なdo、dotimes、dolistに加えて、強力なloopマクロが提供される。loopは、繰り返し、収集、条件分岐を単一のフォーム内で宣言的に記述できる。
2.2.2 非局所脱出(catch/throw, block/return-from)
catchとthrowは動的スコープでの非局所脱出を提供する。catchでタグを設定し、throwでそのタグを持つ最も内側のcatchにジャンプする。blockとreturn-fromはレキシカルスコープでの脱出を提供し、blockで名前付きブロックを定義し、return-fromでそのブロックから値を返して脱出する。これらはエラー処理や早期終了に使用される。
2.3 関数とラムダ式
関数はdefunで定義され、ラムダ式はlambdaキーワードで無名関数を生成する。関数は第一級オブジェクトであり、変数に代入したり、引数として渡したり、返り値として返すことができる。関数の引数には、必須引数、オプション引数(&optional)、キーワード引数(&key)、残余リスト(&rest)など、多様な指定方法がある。
2.4 マクロシステム
2.4.1 マクロの定義と展開
マクロは、defmacroを使用して定義される。マクロはソースコードの抽象構文木(S式)を入力として受け取り、別のS式を出力する関数である。コンパイル時に、マクロ呼び出しはその出力に置き換えられる(マクロ展開)。これにより、言語の構文を拡張し、繰り返しパターンを抽象化できる。
2.4.2 マクロによるDSLの構築
Common Lispのマクロシステムは、ドメイン固有言語(DSL)の構築に強力である。例えば、loopマクロ自体がDSLの一例である。さらに、with-open-fileのようなリソース管理マクロ、defclassのようなオブジェクト定義マクロ、ユーザー定義のDSL(例:HTMLテンプレート、テストフレームワーク)が容易に作成できる。マクロはバッククォート構文(`、,)を使用して、テンプレートからのコード生成を簡潔に記述する。
3 プログラミングパラダイム
3.1 関数型プログラミング
3.1.1 高階関数とクロージャ
高階関数(例:mapcar、reduce、remove-if)は他の関数を引数に取るか、関数を返す。クロージャは、レキシカルスコープの変数を捕捉する無名関数であり、状態を保持する関数オブジェクトとして機能する。lambda式とレキシカルスコープの組み合わせにより、カリー化や部分適用などの関数型テクニックが実現される。
3.1.2 遅延評価とストリーム
Common Lispはデフォルトで正格評価だが、マクロや関数を使用して遅延評価を実装できる。遅延ストリームは、処理中の無限リストや計算を表現するために使用される。サードパーティライブラリ(例:series)や自作マクロにより、パイプライン処理やジェネレータを実現できる。
3.2 オブジェクト指向(CLOS)
3.2.1 クラスとインスタンス
CLOS(Common Lisp Object System)は、クラスベースのオブジェクト指向を提供する。クラスはdefclassで定義され、スロット(インスタンス変数)とそのアクセサメソッドを持つ。インスタンスはmake-instanceで生成され、スロットへのアクセスはslot-valueまたはアクセサ関数を使用する。クラスは多重継承をサポートし、標準クラスとユーザー定義クラスが存在する。
3.2.2 総称関数と多重ディスパッチ
メソッドは総称関数(generic function)に属し、defgenericで宣言され、defmethodで実装される。メソッドは引数のすべての型に基づいてディスパッチされる(多重ディスパッチ)。これにより、従来の単一ディスパッチでは難しい、複数のオブジェクトの型に依存した処理が自然に記述できる。
3.2.3 メソッドの組み合わせ(:before, :after, :around)
CLOSでは、メソッドに修飾子(qualifier)を付与できる。:beforeメソッドは主メソッドの前に実行され、:afterメソッドは後に実行され、:aroundメソッドは主メソッドの実行をラップする。これらは複数指定可能で、継承階層に沿って適用される。標準のメソッド組み合わせ(standard method combination)は:before、:after、:aroundをサポートし、カスタムメソッド組み合わせも定義可能。
3.3 メタプログラミング
3.3.1 リフレクション
Common Lispは実行時にプログラムの構造を検査・変更できる強力なリフレクション機能を提供する。describeやinspectでオブジェクトの内部を調べ、symbol-functionやsymbol-plistでシンボルのプロパティにアクセスする。また、arglistやfunction-lambda-expressionで関数の情報を取得できる。
3.3.2 コード生成とコンパイル時計算
マクロに加えて、evalやcompileを使用して実行時にコードを生成・評価できる。load-compiledやfaslファイルでコンパイル済みコードの動的ロードが可能。コンパイラマクロ(define-compiler-macro)やインライン宣言により、コンパイル時の最適化を制御できる。
4 開発環境とツール
4.1 REPLとインタラクティブ開発
Common Lispの中核的な開発様式は、Read-Eval-Print Loop(REPL)を使用したインタラクティブなプログラミングである。REPLでは、任意の式を入力して即座に評価・結果を確認でき、関数の再定義や変数の変更がそのまま実行中プログラムに反映される。このため、ボトムアップ開発やプロトタイピングに非常に適している。
4.2 Emacs + SLIME
SLIME(Superior Lisp Interaction Mode for Emacs)は、EmacsテキストエディタでLisp開発を行うための統合環境である。REPL、エディタ、デバッガがシームレスに連携し、効率的な開発フローを提供する。
4.2.1 ソースコードナビゲーション
SLIMEは、関数定義へのジャンプ(M-.)、参照の検索(M-,)、変数の補完(M-TAB)などのコードナビゲーション機能を提供する。slime-arglistで関数の引数リストを表示し、slime-documentationでドキュメントを検索できる。
4.2.2 デバッガとインスペクタ
エラー発生時は、SLIMEデバッガが自動的に起動し、バックトレースの表示、条件の検査、インタラクティブなデバッグが可能。インスペクタ(inspector)を使用して、データ構造の内部を詳細に調査できる。
4.3 ビルドツールとパッケージ管理(Quicklisp, ASDF)
ASDF(Another System Definition Facility)は、プロジェクトの定義とビルドを管理する標準的なシステム。Quicklispは、サードパーティライブラリの自動ダウンロードと依存関係解決を提供するパッケージマネージャであり、ql:quickloadで簡単にライブラリをインストール・ロードできる。これらにより、再利用可能なコードの管理が容易になる。
4.4 プロファイリングと最適化
実行時のパフォーマンス問題を特定するために、timeマクロで簡易な計測ができる。より詳細なプロファイリングには、sb-profile(SBCL固有)やcl-profilerなどのライブラリが利用可能。最適化に関しては、declareによる型宣言(fixnum、double-floatなど)やoptimize宣言(speed、safety、compilation-speedのトレードオフ制御)が重要である。
5 主要な実装
5.1 SBCL(Steel Bank Common Lisp)
SBCLは、派生元のCMUCLを基盤とするオープンソース実装で、高性能なネイティブコードコンパイラを備える。実行速度が速く、動的最適化に優れ、豊富なデバッグ情報を提供する。Linux、macOS、Windowsなど多数のプラットフォームをサポートし、現在最も広く使われているCommon Lisp実装の一つである。
5.2 Clozure CL(CCL)
CCLは、Macintosh由来のオープンソース実装で、特にmacOSとLinuxでの動作がスムーズである。SBCLに次ぐパフォーマンスを持ち、並列処理(スレッド)のサポートが充実している。LispWorksやAllegro CLに近い開発体験を提供する。
5.3 LispWorks(商用)
LispWorksは、商用サポートと、グラフィカルユーザーインターフェイス(Common Graphics)、データベース接続、プロファイリングツールなどのアドオンを提供する。プロフェッショナルなアプリケーション開発向けで、特に企業での利用が多い。
5.4 Allegro CL(商用)
Allegro CLは、フランツ社が開発する商用実装であり、20年以上の歴史を持つ。高速なコンパイラ、強力なIDE(Allegro Composer)、豊富なサポートライブラリを提供し、大規模システム開発に適する。
5.5 ECL(Embeddable Common Lisp)
ECLは、C言語のランタイム上に実装されたオープンソースのCommon Lispであり、軽量で組み込み用途に特化する。他のCプログラムにLispインタプリタを埋め込んだり、Cとの相互運用が容易で、スクリプト言語としての利用にも適する。
6 応用分野
6.1 人工知能と知識表現
Common Lispは、AI研究の長い歴史の中で使われ続けてきた。知識表現(フレームシステム、セマンティックネットワーク)、ルールベースシステム(CLIPSのLisp版)、自然言語処理(Paip Natural Language Toolkit)などで利用される。特にシンボリック処理や、複雑な推論ルールの実装に強みを発揮する。
6.2 Web開発(Hunchentoot, Caveman2)
Hunchentootは、Common Lisp製のWebサーバで、HTTPリクエスト処理、セッション管理、テンプレートシステムを提供する。Caveman2は、Webアプリケーションフレームワークで、MVCアーキテクチャ、ルーティング、データベース接続を統合的に提供し、RESTful APIや動的Webサイトの開発をサポートする。
6.3 データサイエンスと数値計算
数値計算ライブラリ(cl-num-utils、lparallel、magicl)や、統計解析、機械学習ライブラリ(cl-react、clml)が存在する。gold-utilsやcl-storeによるデータ永続化も可能である。Common Lispの動的型付けと対話的開発は、データ探索に適している。
6.4 ゲーム開発(例:Xelf, Trial)
Xelfは、Common Lispで書かれた2Dゲームエンジンで、SDLベースのグラフィックスと物理演算を提供する。Trialは、よりモダンなゲーム開発フレームワークで、OpenGLによるレンダリング、シーン管理、スクリプティングをサポートする。これらのフレームワークは、Clozure CLやSBCL上で動作する。
6.5 スクリプト言語としての利用
Common Lispは、シェルスクリプトやバッチ処理の代わりとしても利用される。ECLやSBCLのスクリプトモードにより、.lispファイルを直接実行可能。Buildappやshashtを使ってスタンドアロン実行ファイルを作成できる。テキスト処理(正規表現、ファイル操作)も標準で充実している。
7 コミュニティとリソース
7.1 標準化団体とメーリングリスト
ANSI Common Lisp標準の維持は、非公式のコミュニティベースで行われている。主要なメーリングリストとして、pro@common-lisp.net(プロジェクト発表)、lisp-jobs@common-lisp.net(求人)、openmcl-devel(CCL開発)が存在する。定期的に国際Lisp会議(International Lisp Conference, European Lisp Symposium)が開催されている。
7.2 オンラインフォーラム(r/lisp, #lisp IRC)
Redditのr/lispは、Common Lisp全般に関する議論、質問、プロジェクト発表が行われる活発なコミュニティである。IRCの#lispチャンネル(Libera.Chat)は、リアルタイムの質問対応や開発者交流の場であり、世界中のLispプログラマが集まる。
7.3 主要な書籍とチュートリアル
- 『Common Lisp the Language 2nd Ed.』(Guy L. Steele Jr.): 標準仕様書的な位置づけ。
- 『Practical Common Lisp』(Peter Seibel): 実践的なアプローチでLispを学べる入門書。
- 『On Lisp』(Paul Graham): マクロと高度なテクニックに焦点を当てた書籍。
- 『Land of Lisp』(Conrad Barski): ゲーム開発を通じて楽しく学ぶチュートリアル。
- 『Common Lisp Recipes』(Edmond Weitz): 多数の実践的なレシピ集。
7.4 著名なライブラリ(Alexandria, Bordeaux-Threads, Iterate)
- Alexandria: ユーティリティ関数の集合(リスト操作、シーケンス操作、文字列処理、数値関数)。
- Bordeaux-Threads: スレッド処理の抽象化ライブラリで、複数の実装上で一貫したAPIを提供。
- Iterate: ループマクロの代替として、より宣言的で拡張可能なイテレーションを提供。
- CFFI(C Foreign Function Interface): C言語との相互運用を容易にするライブラリ。
- cl-json: JSONデータの読み書きのためのライブラリ。
- Drakma: HTTPクライアントライブラリ。