2015年6月12日
なぜLispなのか?― “データ” と “コード”
(2015-05-07)by Ron Garret
本記事は、原著者の許諾のもとに翻訳・掲載しております。
たくさんの人から 私が昨日Hacker Newsに書いたコメント についてもっと詳しく説明してほしいというメッセージをいただきました。例えば以下のような質問です。
Lispは単なる表記法の1つにすぎないと私は考えますが、間違っているでしょうか? Lispのコードをデータ構造にマッチさせるのがなぜそんなに重要なことなのか理解できません。(おそらく、そのマッチさせるという行為がなぜLispを使うのかという答えになるのだとは思いますが。)私はマクロの大事な何かを見落としているでしょうか? 何か私が気付いていないことがあるでしょうか?
この質問に答えるには少し長くなりそうだったので、ブログに投稿することにしました。以下が私の答えです。
手短に言えば、Lispは 単なる 表記法の1つではなく、プログラミングとは 何か という考え方を根本的に覆すものなのです。プログラミングに関する主流な考え方は、「 プログラミング とは、 データ という人工物に対して、それを処理するスタンドアローンな人工物である プログラム を生み出すことからなる」というものです。もちろんプログラムは データ であるということは誰しもが知っている事実ですが、上記の主流な考え方は 2つのコンセプトの人為的な区別を主張 するものです。そうです、プログラムはデータですが、 コンパイラという名の特殊なプログラムのためだけに存在するデータ ということになります。コンパイラは書くのが難しく、勉強をする必要があります。ただし、自分自身でコンパイラを書く人は(アカデミックな演習をする以外では)少ないでしょう。大抵の人は、そんなおもちゃのようなコンパイラではなく、熟練された技術を有する専門家が書いたコンパイラを使用します。
Lispのプログラミングは、マシンとのより一般的なインタラクションです。マシンに何をして欲しいかを 記述する という行為は、あなたが記述したことのマシンによる実行、その結果の観察、その観察を元に加えられた「あなたがマシンにしてほしいこと」の記述の変更と交互します。つまり、どこまでやればプログラムが完成するとか、どこでそれがスタンドアローンな人工物になるという明確なラインはありません。しかし、Lispを使えばそのような明確なラインを引くことができますし、スタンドアローンな実行ファイルを生成することができます。これはC言語でインタラクティブなプログラムを書くことができるのと同じようなものです。しかし、LispはAIのリサーチを目的として開発されたので、 意図的に インタラクティブに作られています。一方でC言語はプログラムのオペレーションを目的に開発されたので、インタラクティブではありません。Lispと違ってC言語にとってスタンドアローンな実行ファイルを作成することがネイティブであるように、C言語と違ってLispにとってはインタラクティブ性はネイティブです。
もちろん反復する以外に選択肢がない場合もあるでしょう。時には完成されたデザインを生成するのに必要な知識が十分でなかったり、実験を行わなければならなかったり、スピードが命の時もあるでしょう。このような場合には小さなプログラムを組み合わせて大きなプログラムにするような一般的な仕組みが便利です。C言語の世界ではそのような仕組みが存在します。それがpipeです。しかし、C言語は階層的なデータをシリアライズ/デシリアライズをする標準的な方法を 持ち合わせていません 。その代わりC言語は、様々な種類のシリアライゼーションフォーマットを持っています。Fixed-Width、delimiter-separated、MIME、JSON、ICAL、SGML とそこから派生したもの、HTML 、XMLなどです。これでもごく一部です。これらは単なる、 データ のシリアライゼーションフォーマットにすぎません。 コード の記述に使うプログラミング言語はそれぞれ独自のシンタックスと特異性を保持しています。
C言語の生態系はシンタックスが 重要である という奇妙な考え方を生み出しました。シンタックスのデザインにはたくさんのエネルギーが注がれていますし、LEXやYACCなどのツールが広く使われています。C言語の世界では、パーサを書くことがプログラマの仕事の大半を占めます。
今も昔もC言語に携わる人なら、 コード を表現するのにシリライズフォーマットのデータの1つを使ってみよう という考えに一度は至るでしょう。しかしその努力はすぐに終わりを迎えます。XMLやJSONで表現されたコードは、コードを表現するためにデザインされたシンタックスを使って表現されたコードに比べると、絶対的に ひどい ものになるからです。結局、データとしてコードを表現するのは得策ではなかったと気付き、パーサを書くことになるのです。
しかし、それは間違いです。
XMLやJSONで表現されたコードがひどいものになるのは、データとしてコードを表現するのが得策ではなかったということではなく、XMLとJSONが シリアライズフォーマットしてうまくデザインされていない からです。つまり、句読点が多すぎるのです。XMLの場合は余計なものが多すぎます。Lispが他のシンタックスと違ってコードを表現するのに優れているのは、S式のシンタックスがシリアライズフォーマットとしてよくデザインされているからです。S式は 最小限 のコードで書くことができます。3つを比べると以下のようになります。
XML: <list><item>abc</item><item>pqr</item><item>xyz</item></list>
JSON: ['abc', 'pqr', 'xyz']
S式: (abc pqr xyz)
XMLはこんなにシンプルな例でも明らかにひどく長ったらしくなってしまいました。JSONとS式はあまり変わらないようにも見えますが、よく考えてください。以下のような場合、S式は本領を発揮します。
(for x in foo collect (f x))
同じものをJSONで書くと以下のようになります。
['for', 'x', 'in', 'foo', 'collect', ['f', 'x']]
これをXMLにするとどうなるかは、みなさんの宿題にします。
ただ式を眺めるのではなく、実際に タイプ してみると、その違いがより明確になります。(ぜひ試してみてください。)小規模なデータ構造には害がなさそうに見える引用符やカンマは、非常に複雑なデータ構造にとっては即座に耐えがたい負担となるのです(そして、XMLのようにSGMLから派生した言語では、完全にお手上げです)。
Lispが非常に素晴らしく効果的な理由は、Lispを使う人がコードをデータとして表現しようとする直感が実際に 正しい からです。 Lisp こそ非常に 有効な手段です。特に、Lispを使うと、インタプリタやコンパイラを書くのが 本当に簡単 になります。そして、C言語の世界でパーサを書くのが通常の仕事であるのと同じくらい、Lispのプログラミングにとって、新しい言語を生み出し、そのためにインタプリタやコンパイラを書くことが当たり前になります。しかし、簡単にインタプリタやコンパイラを書くためには、 コードとデータを表現する正しい構文から始める 必要があります。つまり、コードとデータを表現する最小構文から始めるのです。それ以外のものから始めると、大量のカンマや引用符、山括弧で訳が分からなくなってしまうでしょう。
そうなると、まずはS式から始める必要があります。S式 こそ が階層データを表現する最小構文だからです。 考えてみてください。階層データを表現するために必要なのは、トークンセパレータとブロックデリミタという2つの構文要素のみです。S式では、スペースがトークンセパレータで、丸括弧がブロックデリミタになります。それだけです。他のやり方では、ここまで構文要素を減らすことはできません。
Lispで丸括弧がよく目につくからといって、Lispが他のプログラミング言語に比べて丸括弧を多用しているわけではないということは特筆に値するでしょう。Lispには1つしかブロックデリミタ(丸括弧)がないため、丸括弧が目につきやすくなるのです。その他の言語は、区切られるブロックの種類によってさまざまなブロックデリミタを使っています。例えば、C言語ファミリは引数リストや部分式に丸括弧、配列に角括弧、コードブロックや辞書には波括弧を使います。また、カンマやセミコロンをブロックデリミタとして使用します。両者を比較してみると、大抵Lispの方がCのような言語よりもブロックデリミタが 少ない です。特にコールバックがいたるところにあるJavaScriptでは、デリミタが頻繁にあらわれ、よく深みにはまります。そして、文脈によって異なる 正しい デリミタを把握するのは、プログラマにとって頭を悩ませる問題です。Lispを使うプログラマは、そんな心配をする必要がありません。ブロックを閉じる場合は、閉じ括弧をタイプするだけです。 常に 頭を悩ます必要がないので、Lispを使うプログラマは思考力に余裕があり、本当に自分たちが解決したい問題に集中することができます。
そのことに関しては、コーディングに立ち戻った方がよさそうですね。もちろん何度も繰り返しです。
株式会社リクルート プロダクト統括本部 プロダクト開発統括室 グループマネジャー 株式会社ニジボックス デベロップメント室 室長 Node.js 日本ユーザーグループ代表
- Twitter: @yosuke_furukawa
- Github: yosuke-furukawa