前書き

この本はLispプログラマとして成長したいと思っている全ての人に向けて書かれた. 読者が既にLispに親しんでいることを前提としたが, 長いプログラミング経験が必ずしも要る訳ではない. 始めの数章はかなりの量の復習を含んでいる. これらの章はきっと熟練Lispプログラマにも面白く思ってもらえるだろう. そこでは見慣れた話題に新しい光を当ててみたからだ.

あるプログラミング言語のエッセンスを一文で伝えるのは難しいが, John Foderaroの言葉はかなりそれに近い:

Lispはプログラム可能なプログラミング言語である.

Lispはそれだけのものではないが,Lispを自分の意図に従わせることのできる能力は Lispエキスパートと初心者との違いの大部分を占める. 熟練Lispプログラマは,プログラミング言語に従ってプログラムを書くのと同じように 自分の書くプログラムに向けてプログラミング言語を構築していく. この本では,Lispが元々適しているボトムアップ・スタイルでのプログラミング方法を教える.

ボトムアップ・デザイン

ボトムアップ・デザインは, ソフトウェアがどんどん複雑化していく中で一層重要になってきている. 現代のプログラムは,とてつもなく複雑だったり, ときには解釈が定まらないような仕様を満たさなければならない. そのような状況では伝統的なトップダウン方式が破綻してしまうことがある. そこで,現在のほとんどのコンピュータ科学講座で教えられているのとは 大変異なったプログラミング手法が発達した: ボトムアップ・スタイルだ. そこではプログラムは一連の層として記述され, それぞれの層がその1段上に対して一種のプログラミング言語として機能する. そうして書かれたプログラムの例はX WindowsとTeXだ.

この本のテーマは二つある. Lispがボトムアップ・スタイルで書かれるプログラムに対して 自然なプログラミング言語であるということと, ボトムアップ・スタイルがLispプログラムを書くのに自然な方法であるということだ. だからOn Lispは2種類の読者にとって興味深いものになるだろう. 拡張可能なプログラムを書くことに興味のある人には, この本は適切なプログラミング言語があれば何ができるのかを示す. Lispプログラマには,この本はLispを最大限に活用するための実用的な説明を提供する.

この本のタイトルはLispでのボトムアップ・プログラミングの重要性を 強調するつもりで付けられた. プログラムをただLispで書くだけでなく, 独自の言語をLispを基に(on Lisp)書くことが可能であり, プログラムはその言語で書くことができる.

どのプログラミング言語でもボトムアップでプログラムを書くことはできるが, このスタイルのプログラミングにはLispは最も自然な器だ. Lispでは,ボトムアップ・デザインは異常に大規模だったり複雑なプログラム専用の, 特別なテクニックではない. しっかりしたプログラムはみな,部分的にはこのスタイルで書かれることになるだろう. Lispは開発当初から拡張可能なプログラミング言語となるよう設計されていた. 言語そのものの大半がLispの関数の集合体であり, それらはユーザ自身の定義によるものと何も違わない. それどころかLispの関数はリストとして表現できるが,リストはLispのデータ構造なのだ. このことは,ユーザがLispコードを生成するLisp関数を書けるということだ.

いいLispプログラマはこの可能性を利用する方法を知らなければならない. そのための方法は普通,マクロと呼ばれる一種のオペレータを定義してみることだ. マクロの理解は,正しいLispプログラムを書くことから 美しいLispプログラムを書くことへ移行する際の最も重要なステップの一つだ. 入門的なLisp本に載っているのはマクロの大雑把な外観程度: マクロとは何かの説明と, ユーザがマクロを使えば可能になる奇妙で素晴らしいことのヒントとなる幾つかの例だ. ここではそういった奇妙で素晴らしい事柄に特に注意してみる. この本の狙いの一つは,そういったユーザ達がマクロに関する経験から 今に至るまでに学ばなければならなかったことを一箇所にまとめることだ.

無理もないことだが,入門的なLisp本はLispと他のプログラミング言語との違いを強調しない. そういった本は(大抵)Pascal用語でプログラムを考えるよう教育されてきた生徒達に メッセージを伝えなければならない. 「LispのdefunはPascalの手続き定義に似ているが, 実際は関数オブジェクトを作るコードを生成するプログラムを書くプログラムであり, 第1の引数として与えられたシンボルを名札に使うのだ.」 と説明しても,混乱を招くばかりだろう.

この本の目的の一つは,Lispと他のプログラミング言語との違いは何かを説明することだ. 私は当初から,他の点がみな同じだったらCやPascalやFortranではなく Lispでプログラムを書いた方がずっといいということは分かっていた. これは単なる好みの問題ではないことも分かっていた. しかし実際にLispが何らかの点でよりいいプログラミング言語だと主張するなら, それがなぜかを説明できるようにした方がいいことに気が付いたのだ.

誰かがLouis Armstrongにジャズとは何かと尋ねたとき,彼はこう答えた. 「ジャズとは何かが人に聞かないと分からないんだったら, 分かるようにはならないだろうね.」 しかし彼はある方法で確かにその質問に答えた: 彼は人々にジャズとは何かを示したのだった. それがLispの力を説明する唯一の方法だ ---他のプログラミング言語では困難だったり,不可能な技をやってみせることだ. プログラミングに関する本の大半が ---Lispプログラミングの本さえ--- どんなプログラミング言語でも書けるような題材を扱っている. On Lispは,Lispでのみ書けるような類のプログラムを主に扱う. 拡張性,ボトムアップ・プログラミング,インタラクティブな開発, ソースコードの変換,埋め込み言語 ---Lispの長所が現れるのはそういった所だ.

もちろん原則的には,Turing機械と等価なプログラミング言語はみな 他のプログラミング言語と同じ作業が可能だ. しかしそういった能力は,プログラミング言語が目的とするものではない. 原則的には,プログラミング言語でできることはみなTuring機械でもできる. しかし実際には,Turing機械のプログラミングは手間をかけるに値することではない.

だから私が「これは他のプログラミング言語では不可能なことのやり方に ついての本だ」と言うとき,数学的な意味で「不可能」だと言うのではなく, プログラミング言語に関わる意味でそう言っているのだ. つまり,この本の中のプログラムの幾つかをCで書かなければならないなら, 始めにCでLispコンパイラを書くことで目的を達しても同じなのだから. 例えばCでPrologを埋め込み言語にしてみる ---それにかかる手間は想像がつくだろうか? 第24章では,180行のLispコードを使ってその方法を示す.

しかし,ただLispの能力のデモンストレーションをする以外にもっとやりたいことがある. なぜLispは他と違っているのかも説明したい. これは実は微妙な問いだ ---非常に微妙な問いなので「記号演算(Symbolic computation)」 などという言葉を答えにすることはできない. 私がこれまでに学んだことはできる限り明快に説明するよう試みた.

この本の方針

関数はLispプログラムの基礎なので,この本の最初の幾つかの章では関数を扱う. 第2章では,Lispの関数とは何かということと,関数のもたらす可能性を説明する. 第3章では,Lispプログラムでの主要なプログラミング・スタイルである 関数プログラミングの長所について議論する. 第4章では,Lispの拡張のための関数の使い方を示す. そして第5章では,別の関数を返す関数を使って定義できる,新たな種類の抽象化を示唆する. 最後に第6章では,伝統的なデータ構造の代わりに関数を使う方法を示す.

この本の残りの章では,関数よりもマクロの方を多く扱う. マクロにはより多くの注意を払った. 一つにはマクロにはもっと語るべきことがあるからで, また書籍では今まで十分に説明されていなかったからでもある. 第7--10章では,マクロに関するテクニックについての完全な手引きを与える. そこまでで,熟練Lispプログラマがマクロについて知っていること ---作用の仕組み,定義方法,テスト,デバッグ,使うべきときとそうでないときの区別, マクロの主要な種類,マクロ展開を生成するプログラムの書き方, マクロのスタイルとLispのスタイルとの一般的な違い, マクロに影響するそれぞれの独特な問題を発見し,修正する方法--- は全て理解できるだろう.

この手引きに続き,第11--18章ではマクロによって可能になる強力な抽象化を幾つか示す. 第11章では,コンテキストを形成したり, 繰り返しや条件分岐を実現する古典的なマクロの書き方を示す. 第12章では,一般化された変数への作用におけるマクロの役割を説明する. 第13章では,マクロを使い,行うべき計算をコンパイル時にやってしまうことで プログラムを速く走らせる方法を示す. 第14章ではアナフォリック(anaphoric)マクロを導入する. これはプログラム内で代名詞を使うことを可能にする. 第15章では,マクロを使って第5章で定義された関数ビルダに 便利なインタフェイスを提供する方法を示す. 第16章では,Lispにユーザのプログラムをユーザのために書かせるために使われる, マクロを定義するマクロの使い方を示す. 第17章ではリードマクロ(read macro)について, 第18章では構造化代入(destructuring)のためのマクロについて議論する.

第19章からはこの本の第4部 ---埋め込み言語のために割かれた部分が始まる. 第19章では,2種類の同じ働きのプログラムを示すことでその話題を導入する. それはデータベースに対する問い合わせ(クエリ)に答えるものだが, 最初はインタプリタによって実装されたものを, そして次には本物の埋め込み言語として実装されたものを示す. 第20章では,Common Lispプログラムに, 計算の残りを表すオブジェクトである継続(continuation)の概念を導入する方法を示す. 継続は非常に強力なツールで,複数プロセスと非決定的選択の両方の実装に使える. これらの制御構造をLispに埋め込む方法は,それぞれ第21, 22章で議論する. 非決定性を使えば予知能力を持っているかのようなプログラムが書けるようになるが, これは不思議な能力の概略のように思えていることだろう. 第23, 24章では, 非決定性がその触れ込みに違わないことを示す埋め込み言語を2個取り上げる: 完全なATNパーサと埋め込みPrologだが,コードは全部で200行余り\note{200lines}になる.

これらのプログラムが短いという事実は,それ自身の中身は何もないということを表している. 不可解なコードを書くことに頼ったなら,200行で可能なことについては何も分からない. これらのコードが短く済むのは,プログラミング上のトリックに頼ったからではなく, Lispが本来意図していた使い方に従って書かれているからなのだ. それこそが重要な点だ. 第23, 24章の中核は,ATNパーサを1ページ分のコードで実装したり, Prologを2ページ分のコードで実装する方法ではない. 最も自然なLispによる実装によれば,それらのプログラムは単にその短さで済むという事実だ. その後の章で使われる埋め込み言語は,最初のポイント二つ ---Lispはボトムアップ・デザインのための自然な言語であることと, ボトムアップ・デザインはLispの自然な使用方法であること--- の例に基づく証明を与える.

この本のまとめは,オブジェクト指向プログラミングの議論と, 特にCLOS (the Common Lisp Object System)だ. この話題を最後に取っておくことで,オブジェクト指向プログラミングがLisp内に既にある 考え方の延長に位置するということを一層はっきりと理解できる. それはLispで構築できる数多くの抽象化構造のうちの一つなのだ.

注釈は\pageref{chap:note}ページから始まり,参考文献案内, 追加または代替用のコードや,その場には直接関係のないLispの側面の説明も含む. またパッケージに関する付録(\pageref{chap:package}ページ)もある.

ニューヨークのツアーが世界の文化の大半のツアーになりうるのと同様, プログラム可能なプログラミング言語としてのLispの学習は Lispテクニックの大部分のスケッチになる. ここで説明されたテクニックの大半はLispコミュニティに知られているが, 今までどこにも書かれていなかったことも多い. またマクロの適切な役割や変数キャプチャの本質といった話題は, 多数の熟練Lispプログラマたちもおぼろげにしか理解していなかった.

Lispはプログラミング言語の一族だ. そのうちCommon Lispはこの先ずっと広く使われる方言(dialect)だろうから, この本の例の大半はCommon Lispで書かれている. この言語は元々1984年にGuy Steeleの本Common Lisp: the Language (CLtL1)によって定義されたものだ. この定義は1990年に第2版(CLtL2)の出版によって改訂された\note{CLtL2}. 今度は将来のANSI標準がその役目を引き継ぐことだろう \footnote{訳注:1996年にANSI Common Lisp(アメリカ公式標準)制定. GCL,Clisp,CMUCL,SBCLなどフリーの実装あり.}.

この本には,式一個から正しく動作するPrologの実装まで,数百の例が載っている. この本のコードのうち, 可能なものはみな全てのヴァージョンのCommon Lispで動作するように書かれている. CLtL1の実装にない機能を必要とするわずかな例は,本文中にその旨を明記してある. 後半の章にはSchemeによる例も幾つかある. これらもはっきりと分かるようにしてある.

コードはendor.harvard.eduのanonymous FTPの pub/onlispディレクトリから手に入る. 質問やコメントはonlisp@das.harvard.eduに送ってほしい.

謝辞

While writing this book I have been particularly thankful for the help of Robert Morris. I went to him constantly for advice and was always glad I did. Several of the examples in this book are derived from code he originally wrote, including the version of for on page 127, the version of aand on page 191, match on page 239, the breadth-first true-choose on page 304, and the Prolog interpreter in Section 24.2. In fact, the whole book reflects (sometimes, indeed, transcribes) conversations I've had with Robert during the past seven years. (Thanks, rtm!)

I would also like to give special thanks to David Moon, who read large parts of the manuscript with great care, and gave me very useful comments. Chapter 12 was completely rewritten at his suggestion, and the example of variable capture on page 119 is one that he provided.

I was fortunate to have David Touretzky and Skona Brittain as the technical reviewers for the book. Several sections were added or rewritten at their sugges-tion. The alternative true nondeterministic choice operator on page 397 is based on a suggestion by David Toureztky.

Several other people consented to read all or part of the manuscript, including Tom Cheatham, Richard Draves (who also rewrote alambda and propmacro back in 1985), John Foderaro, David Hendler, George Luger, Robert Muller, Mark Nitzberg, and Guy Steele.

I'm grateful to Professor Cheatham, and Harvard generally, for providing the facilities used to write this book. Thanks also to the staff at Aiken Lab, including Tony Hartman, Janusz Juda, Harry Bochner, and Joanne Klys.

The people at Prentice Hall did a great job. I feel fortunate to have worked with Alan Apt, a good editor and a good guy. Thanks also to Mona Pompili, Shirley Michaels, and Shirley McGuire for their organization and good humor.

The incomparable Gino Lee of the Bow and Arrow Press, Cambridge, did the cover. The tree on the cover alludes specifically to the point made on page 27.

This book was typeset using LaTeX, a language written by Leslie Lamport atop Donald Knuth's TeX, with additional macros by L. A. Carr, Van Jacobson, and Guy Steele. The diagrams were done with Idraw, by John Vlissides and Scott Stanton. The whole was previewed with Ghostview, by Tim Theisen, which is built on Ghostscript, by L. Peter Deutsch. Gary Bisbee of Chiron Inc. produced the camera-ready copy.

I owe thanks to many others, including Paul Becker, Phil Chapnick, Alice Hartley, Glenn Holloway, Meichun Hsu, Krzysztof Lenk, Arman Maghbouleh, Howard Mullings, Nancy Parmet, Robert Penny, Gary Sabot, Patrick Slaney, Steve Strassman, Dave Watkins, the Weickers, and Bill Woods.

Most of all, I'd like to thank my parents, for their example and encouragement; and Jackie, who taught me what I might have learned if I had listened to them.

この本を読むのはきっと楽しいと思う. 私は,知っているプログラミング言語のうちでLispが一番好きだ. それはただLispが一番美しいからだ. この本は,最高にLispっぽいLisp (Lisp at its lispiest)についての本だ. この本を書くのは楽しかった. その感じが行間に流れていたら嬉しい.

Paul Graham


↑: On Lisp     →: 拡張可能なプログラミング言語

Copyright (c) 2003-2011 野田 開     NODA Kai <nodakai@gmail.com>