原文:Lisp for Web-Based Applications
普通のやつらの上を行けへのリンクがslashdotにアップされた後で、何人かの読者は、私たちがViawebでLispを使ったことで得た技術的な利点について、さらに詳しいことを聞きたがった。 興味を持つ人のために、私が2001年4月にケンブリッジ(MA)のBBN研究室でした話のうち抜粋をここに示す。
ポール・グレアム
(このエッセイは2001年4月にケンブリッジ(MA)のBBN研究室でした話の要約である)
ウェブベースのアプリケーションを書く際にLispを使う理由の1つは、Lispを使えるってことだ。 自分のサーバだけで動くソフトウェアを書いているなら、どんな言語を使うことだってできる。
長い間、アプリケーションを書くのにどんな言語を使用したらよいか、その選択の余地がプログラマにはたいしてなかった。 最近まで、アプリケーションプログラムを書くということはデスクトップコンピュータで動くためのソフトウェアを書くことだった。 デスクトップソフトウェアでは、オペレーティングシステムと同じ言語でアプリケーションを書くように仕向けられる強い傾向があった。 10年前、あらゆる実用的なアプリケーションはCで書かれていた。
ウェブベースのアプリケーションでは、それが変わる。 あなたはサーバを支配することができ、あなたが望む言語でソフトウェアを書いていい。 現在ではオペレーティングシステムとコンパイラの両方のソースコードがあるのを当然のことと考えることができる。 言語とOSの間に何か問題があったら、自分でそれを修正できる。
だが、この新しい自由は諸刃の剣だ。 多くの選択肢があるってことは、どの選択をしたらよいかを考える必要がある、ということだからだ。 昔はもっと単純だった。 あなたがソフトウェアのプロジェクトを担当していて、ある問題児が「今までのとは違う言語でソフトを書こう」って言い出したら、単に「実用的じゃない」って言って、それでおしまい。
現在のサーバベースのアプリケーションでは、すべてが変わった。 どんな言語を選ぶかが、市場の力に影響されることがある。 大多数の競争相手がしているように、単にCとC++を使い、何も変化していないふりをしてしたら、あなたは自分自身が落ちぶれるための準備をしている。 より強力な言語を使用する小さいベンチャーが、あなたのおまんまを食べてしまうだろう。
Lispでのソフトウェア開発にはあるスタイルがある。 伝統的なもののうちの1つがインクリメンタル開発だ。 できるだけ早く、ほとんど何もしないようなプログラムを書くことから始める。 その後、少しずつ特徴を加えるが、どの段階でも動くコードがある。
私はこの方法は、高速に動くものより、よりよいソフトウェアが作れると思う。 Lispにおけるすべてはこのスタイルのプログラミングにチューンされている、なぜってLispプログラマはこの方法で、少なくとも30年以上はうまくいっているからだ。
Viawebエディタはインクリメンタル開発の最も極端なケースの1つだろう。 それは私たちがViaweb社を始める前に書いた本の中でサンプルとして使用した、ウェブサイトを生成する120行のプログラムから始まった。 Viawebエディタはこのプログラムから徐々に成長して、およそ2万5000行のコードになった。 私はけっして仕切りなおして、全体を書き直したりはしなかった。 私は1日や2日でも動くコードがなかったら今のように大きくなっていた、とは思わない。 全体の開発過程は1つの長いシリーズのゆるやかな変化だった。
このスタイルの開発はウェブベースのソフトウェアで可能な、動かしつつのリリースにフィットする。 またそれは一般的に、ソフトウェアを書く、より速い方法だ。
Lispのインタラクティブトップレベルは、早期にソフトウェアを開発することにおいて大きな助けになる。 だが最も大きい利点は、たぶんバグを見つけることに関してだ。 私が前に述べたように、ウェブベースのアプリケーションでは、ユーザのデータはあなたのサーバにあって、通常はバグを再現することができる。 顧客サポートがバグのレポートを持ってエディタで私にくると、私はコードをLispインタプリタにロードし、ユーザのアカウントへログを記録する。 バグを再現できたら、実際にループを中断し、なにが間違っていたかを教えてもらう。 たいていの場合、すぐにコードを修正して修正版をリリースできた。 そして、ユーザがまだ電話をかけているうちに、修正したと言うことができた。 そのような素早いバグフィックスによって、信じられないほど有利な立場に立つことになった。 ユーザがまだ電話をかけている間にバグを見つけて修正することができるなら、「それを想定していたのだ」という印象をユーザに与えることができ、非常に魅力的だった。 そしてときどき(嬉しいことに)、顧客サポートの人々はユーザに「もう一度ログインしてみて、まだ問題があるか調べてください」と言うことができた。 もちろん、ユーザがログインしなおすと、バグが修正された新しいバージョンのソフトウェアがあり、すべてがうまく動いた。 これは少しズルいとは思うが、すごく面白かった。
Lispのマクロは私たちにとって別の大きな勝因だった。 我々はViawebエディタで、非常にはば広くマクロを使った。 Viawebエディタは1つの大きいマクロとして記述できた。 このことで、Lispがどれほと頼りになったかわかるだろう。Lispと同じようなマクロを持っている言語は他にないのだから。
マクロを使ったのはHTMLを生成するためだった。 マクロとHTMLにはとても自然な親和性がある。HTMLはLispのように前置記法で、Lispのように再帰的だからだ。 非常に複雑なHTMLを生成するのに、マクロを定義するマクロを使っており、それでいてそのマクロは、とても扱いやすかった。
マクロの別の大きな用途は、RTMLと呼ばれるページ記述用の埋込み言語だった。 (私たちはRtmlが表すべきもののために様々な説明をしたが、実際に私はそれを、Viawebのもう片方の創設者、ロバートモリスにちなんで名付けた)(創設者のユーザ名はRtmだ)
私たちのソフトウェアによって作られたあらゆるページがRtmlで書かれたプログラムによって生成された。 私たちは人々を脅かさないようにこれらのプログラムをテンプレートと呼んだが、それは正真正銘のプログラムだった。 そして、実際にはそれはLispのプログラムだった。 RtmlはマクロとLispの組み込みオペレータだった。
ユーザは、彼らのページがどのように見えて欲しいかを記述するために、独自のRtmlテンプレートを書くことができた。 私たちには、これらのテンプレートを操作するための構造エディタがあって、多くの事柄はそれらがInterlispに持っていた構造エディタと似ていた。 自由形式テキストをタイプすることの代わりに、コードのビットを一緒にカットペーストした。 これは、構文誤りを得るのが不可能であることを意味した。 また、それは、私たちが基本的なs表現における括弧を表示する必要がないことを意味した: 私たちはインデントで構造を見せることができた。 このようにして私たちは、言語があまり怖くないように見えるように作りました。
また、私たちはランタイムにおける誤りが全くおこらないように、Rtmlを設計しました: あらゆるRtmlプログラムがある種のウェブページをもたらしました、そして、あなたはあなたがそれを意図したページを生産するまでそれをハックすることによって、それをデバッグすることができた。
初めに、私たちはユーザがウェブコンサルタントであると予想しました、そして彼らがRtmlを大いに使用すると予想しました。 私たちはセクションページやアイテムページなどにいくつかのデフォルトテンプレートを用意して、考えではユーザがそれらを手に取り修正して欲しかったどんなページも作るようにした。
実際にはウェブコンサルタントはViawebが好きでなかったと判明した。 コンサルタントは一般に、クライアントが使うには難しすぎる製品を使うことを好んだ、なぜならそれは継続的な雇用を保証するからだ。 コンサルタントは私たちのウェブサイトに来ると、私たちのソフトウェアはとても使うのが簡単で、誰でも5分でオンラインストアを作れると言うだろう、そして私たちがそれを使用する方法は全くないと言うだろう。 したがって、 私たちはウェブコンサルタントから多くの関心を得なかった。 代わりにエンドユーザとなるユーザは皆、実際の商人である傾向があった。 彼らは自分のウェブサイトを制御できるという考えが非常に好きであった。 そして、このようなユーザはまったくプログラミングをしたいとは思っていなかった。 彼らはただデフォルトテンプレートを使用した。
したがって結局、Rtmlはプログラムへの主なインタフェースにはならなかった。 それは2つの役割を果たした。 まず、それは卓越したユーザにとっての抜け道だった、彼らは私たちの内蔵のテンプレートが提供することができなかったものが欲しかったのだ。 Viawebをすることの間の どこかでは、だれかが非常に役に立つ忠告を私に与えました: 彼らは原則としてそれを決して取らないでしょうが、ユーザはいつもアップグレード経路が欲しいです。 Rtmlは私たちのアップグレード経路でした。 もしあなたがそうしたいなら、あなたのページのすべてに絶対の制御ができるのだ。
数百人のユーザのうちの1人だけが実際にそれら自身のテンプレートを書いた。 そして、それがRtmlの2番目の利点に通じました。 これらのユーザが私たちの内蔵のテンプレートを変更した方法を見ることによって、私たちは何を加える必要があったのを知った。 最終的に、誰もRtmlを使用しないですむようにすることを私たちは目標にしました。 私たちの内蔵のテンプレートは人々が望むすべてをするべきだった。 この新しいアプローチの中で、Rtmlは私たちのソフトウェアに何か欠けているかという危険信号を知らせてくれた。
Rtmlを使用したことから得た3番目の、そして最も大きい勝利は私たち自身がそれから得た利点だった。 私たちがRtmlを使用した唯一の人々であったとしても、そのようにソフトウェアを書くのはたいへん価値があった。 私たちのソフトウェアに追加の抽象化レイヤーを持つことは、競争相手より大きい利点を私たちにもたらした。 ひとつには、私たちのソフトウェアをよりクリアにデザインした。 私たちの競争相手のようにウェブページを発生させる実際のCやPerlコードのビットを持つ代わりに、ウェブページを生成するための非常に高レベルな言語と、それで指定された私たちのページのスタイルがありました。 それはコードをよりきれいにし、変更すを容易にした。 私は、ウェブベースのアプリケーションは一連の多くの小さい変更としてリリ ースされると既に言及しました。 あなたがそれをすると、あなたは何か与えられた変更がどれくらい重大であるかを知ることができるようになりたがっています。 あなたのコードをレイヤーに分割することで、あなたはこれにより良いハンドルを乗せます。 下のレイヤー(Rtml自身)でものを変更するのは、めったに、じっくり考えた後で行われる重大な問題だった。 トップレイヤー(テンプレートコード) の変更は、結果を心配することなく素早くすることができた。
RtmlはまさしくそのLispy命題だった。 まず第一にそれはほとんどLispのマクロだった。 オンラインエディタは、s-表現を操りながら、場面の後ろにいた。 そして人々がテンプレートを動かすと、コンパイルを実行時に呼び出すことによりLispの関数にコンパイルされたものを得た。
Rtmlは私が常々 Common Lisp の怪しい特徴だと考えていたキーワード・パラメータにも大いに依存した。 ウェブベースのソフトウェアがリリースされる方法のために、あなたは変更するのが容易なようにソフトウェアを設計しなければならない。 そして、Rtml自身もソフトウェアの他の部分と同様に変更が容易でなければならなかった。 Rtmlのオペレータの大部分はキーワード・パラメータを取るように設計されて、判明したすべてのヘルプがそうするのをそうされます。 オペレータのひとつの挙動に別次元を加えたいと思うなら、私はただ新しいキーワード・パラメータを加えることができ、みんなの既存のテンプレートは働き続けるだろう。 Rtmlオペレータのいくつかはキーワード・パラメータを取らなかった、なぜならそれらを今後絶対変更しないと思ったから、そしてそのほとんどすべてであとでひどい仕打ちを受けた。 もし戻って最初からやり直すことができるなら、私が変えるものの1つは私がすべてののRtmlオペレータにキーワード・パラメータを取らせるようにするだろう。
事実上、私たちはエディタの中に2、3の埋め込まれた言語を持っていた。 別のもの(私たちは直接ユーザに露出しなかった)は、画像を記述するためのものだった。 Viawebは画像の記述を入れると画像を作成しそのURLを返すCで書かれた画像ジェネレータを含んでいた。 私たちは、また、これらの画像を記述するのにs表現を使った。
ウェブページをUIとして使用することの問題の1つはウェブセッションが本来ステートレスだということである。 私たちは、サブルーチンのような振舞いをシミュレートするのにレキシカルクロージャを使用することによって、これを回避した。 あなたがコンティニュエーションを理解しているなら、私たちがしたことについて説明する1つの方法は私たちはコンティニュエーション渡し形式でソフトウェアを書いたということだ。
ほとんどのウェブベースのソフトウェアは、リンクを生成するとき「ユーザがこのリンクをクリックしたら、このcgiをこの引数で呼び出して欲しい」、と思っている。 私たちのソフトウェアがリンクを生成するとき、「ユーザがこのリンクをクリックしたら、このコードを走らせたい」と思うかもしれない。 そして、コードの断片はことによると、値が周囲の文脈からきた自由な変項を含む任意のコードである可能性がある(実際はほとんど)。
私たちがこれを行った方法は、クロージャであると予想される初期値を持ち、コードの本体が続くマクロを書くことだった。 そのコードはグローバルなハッシュ表にユニークなIDで格納され、本体のコードから生成されたあらゆる出力はそのハッシュキーを含むURLのリンクになった。 そのリンクがクリックされた次のものであるなら、私たちのソフトウェアは、対応するコードの断片を探し呼び出すだろう、そして連鎖は続く。 事実上私たちはcgiスクリプトをその場で書いていた、それらが周囲の文脈を示すことができたクロージャであったのを除いて。
これはとても空論に聞こえるので、このテクニックが明白な違いを作ったという例をあげる。 あなたがウェブベースのアプリケーションでしばしばしたいことの1つは様々なタイプのプロパティがあるオブジェクトを編集することだ。 オブジェクトのいくつものプロパティはフォームのフィールドやメニューとして表すことができる。 例えば人をあらわすオブジェクトを編集しているとして、名前のためにフィールドを取るだろう、タイトルにおいて、特選しているメニュー。
ここで、あるオブジェクトに色というプロパティがあるとき、どうなるだろうか? 下部にある更新ボタンですべてが1つのフォームで起きる、普通のcgiスクリプトを使っていたら、あなたは苦労するだろう。 テキストフィールドを使用して、ユーザにrgb番号をタイプさせることもできるが、エンドユーザはそれが好きではない。 可能な色のメニューを持つこともできるが、そうすると可能な色を制限しなければならない、そうでなければ単に標準のウェブのカラーマップを提供するためだけに、256個のほとんど区別できない名前のメニュー項目が必要になる。
Viawebでは、「変更」と書かれたボタンによって現在の値を表現する見本として色を見せることができた。 ユーザが変更ボタンをクリックすると、選択できる色のイメージマップのページへ飛ぶ。 色を選ぶと、オブジェクトの色が変わってプロパティの編集画面に戻ってくる。 これが私がサブルーチンのような振舞いをシミュレートする、という意味だ。 ソフトウェアはまるで色を選んだところから戻るかのように振舞うことができた。 もちろんそうではない; それはスタックを戻るように見える、新しいcgi呼び出しである。 しかしクロージャを使うことで、ユーザに、また私たちにも、単にサブルーチンコールをしているように見せることが出来た。 私たちは、 「ユーザがこのリンクをクリックしたら、色選択ページに行き、そうしたらここに戻って来い」というコードを書くことが出来た。 これは単にこの可能性のアドバンテージをとったうちのひとつである。 それにより私たちのソフトウェアは私たちの競争相手のものより明らかに洗練されるようになった。