Lisp:よくある正解

Lisp:よくある正解

Lispについての正しい認識と、それでもLisperがLispを使う理由

yoriyukiさんのエントリがなかなか 真実を突いていたので、ネタにさせていただきます。

原因のほとんどは経路依存性とかネットワーク効果によるもので、Lisp自体の性質とは無関係だと思います。と言った上で、私が何となくLisp系言語を使わない理由としては、

  1. Too dynamic: 実行時にコードが差し替えられることがすごい利点だ、と言っている人がいましたが、逆に言えば今どのコードが走っているか理解しにくい、という欠点にも繋がる。
  2. Meta programming:S式のおかげでMeta Programmingがしやすいが、Meta Programmingを多用したプログラムは理解しにくい。
  3. 動的型付け:利点でもあるけど、特有のバグを引き起こす。
  4. 識別子に関数と値の2種類が別々にバインドできる。これは私には非常に美しくなく感じます。主観ですが。一方でSchemeは言語仕様が弱い。(特にModule機能が標準でない)
  5. Proprietaryな実装が中心。CMU LispはStandaloneなネイティブコードを生成してくれない(?)
  6. 上記の利点の大半はRubyやErlangで実現されているし、これらはOOやマルチスレッドというEncapsulationに都合のいい機能を言語自体の機能として盛り込んでいる。
  7. (Lisp一般とは違うけど)EmacsLispの印象が悪い。スタックのサイズがデフォルトではかなり制限されていたり、設定を単に変数の値を(動的に)変化させて行うあたり。

まとめると、利点が欠点でもあること、自由度がありすぎることあたりが気に入らない点かな。間違いなどありましたらご指摘ください。

いや全くその通りなのです。 しかし、それでもLispから離れられない事情というものがLisperにはあるので ございます。

なお、以降の記述はShiroの個人的主観によるものなので、Lisperが皆 そうだというわけではありません。たぶん。

Too dynamic

そう、特に実行中のサーバにつないでREPLプロンプト出して関数を 置き換えちゃったりした場合、ソースに反映しておくのを忘れると 後で痛い目に遭います(経験済)。しかし…

電話の向こうでプロデューサが「2時間後のミーティングまでに この機能をfixして!」と悲鳴を上げていて (前の打ち合わせではそんな仕様言ってなかったじゃん!)、 しかしサーバには全社から毎秒毎秒休み無しにqueryが飛んで来ていて、 今ここで5分でも止めたら全フロアから悲鳴が上がるであろう---とか、

生放送本番中、アニメーションサーバは数分後に来る次のキューに備えて待機中、 しかし突然予想外のデータ修正が入った! もはやCGシステムを再起動している余裕は無い!---とか、

午前4時。あと数時間でユーザが出勤してくる。それまでにシステムをまがりなりにも 動く状態にしておかねば… どうもサードパーティ製のあるライブラリ(ソース無し)の 挙動が怪しいのが問題の原因のようだが、APIにどういうパラメータを与えた時に バグが再現できるか絞りこめていない。もちろんサポートが開くのは明朝、それでは 間に合わないッ。だがッ! ライブラリの内部のみで使われる ある関数に、まれに異常な引数が渡っていることが分かったッ! この内部関数の呼び出しをフックして引数を修正すればとりあえず動かせるッ!---とか、

そういう状況において、「どんなに汚くても、打てる手段がある」というのは 何物にも替え難い救いなのです。というより、そういう予想外の事態に対して エスケープポッドが備えられていない処理系を使うなんて恐くて出来ません。 Lisperは臆病なんです。

いや、そふとうぇあえんじにありんぐ的に言えば正しくないのは重々承知なんですが、 そうも言ってられない事態が「今、そこに」ある場合に何が出来るかって話です。

もちろん、修正のたびにじっくりテストしてシステムを止めて置き換えられる環境とか、 本番系と待機系を備えてノンストップでフェイルオーバーする仕組みまで 作りこめるだけの人と予算がある環境とかであれば、敢えてLispを使うメリットは 無いと言えるかもしれません。

Metaprogramming

小チーム、多くの場合はほぼ一人で作業することが多いLisper。 しかしプロデューサは無理難題をふっかけて来る (それどう考えてもあと2人は必要だよ…)。 追い詰められたLisperが手を出す禁断の果実、それがMetaprogramming。

中でも秘中の秘とされる「macro generating macros」は、凡そ本人にも 理解不可能なコードが容易に書ける恐れがある半面、コード量を数十分の一に 圧縮することも不可能ではないとなれば、誘惑に負けてしまうLisperがいても 不思議ではありません。Lisperは心が弱いんです。

それから、ミーティングの度にころころ変わる仕様。その都度サーバ側(Lisp)と クライアント側(Perl, Scheme, Java, ...)をいじってなんかいられません。 当然、仕様記述からソース生成でしょう。仕様記述の文法なんてわざわざ 設計しなくてもS式にしといて、さらにクロージャ呼べるようにしとけば 想定外の変更にもかなり対応できると。

人はそれをツギハギのシステムと呼ぶかもしれませんが、(将来の)自分が 楽になるためなら何でもする。そう、Lisperは自己中なんです。

ああ、そういえば。Lispの変数名がやたら長くなりがちなのは、そうやって 込み入ったシステムを書いて、しかもドキュメントを書く暇もなくて、 ソースのみからロジックを解読するはめになる将来の自分へ、 せめてもの手がかりを残そうとする足掻きなのかもしれません。 (書くときはEmacsが補完してくれるしね)

あるいは、手元のコンパイラが間抜けで、自然な記述で書くと必要な性能が出ない。 コンパイラの性能向上を待っている暇はない。 でもコンパイラが最適化しやすいコードをわざわざ人間様が書くなんて耐えられない。 ならば、自然な記述を間抜けコンパイラでも分かるように自動変換してやろうではないか、 とかね。

Gaucheのコンパイラcompile.scmもこのひとつの例でして。Gaucheの コンパイラで性能が出るように、マクロが多用というか濫用されています。 (上に書いた、マクロを生成するマクロ、仕様記述からのコード生成、 最適化のための変換、すべて入ってます)。 手続きによる抽象や、Gaucheのオブジェクトシステムを使っても、すっきりと 動くものを書けたでしょう。ただ、性能は今の半分とか1/3になっていたでしょう。 コンピュータサイエンスではこんなのはconstant factorなんで無視できるんですが、 現実には、このfactorが1か1/2かで、「使える」か「使えない」かが はっきりと分かれるんです。

動的型付け

滅多に実行されないコードパスに潜んだバグが実行時型エラーとして報告される、 これはLisperにとっての悪夢です。 まあ、ちゃんとしたCL処理系なら未定義の自由変数の参照とか引数の数の不一致 なんかはコンパイル時に警告を出してくれるんですが、どうせなら型推論もやって 矛盾を見つけたら警告してくれればなと思わなくもない。性能を出したい箇所では 型宣言している場合も多いわけですから。

かと言って、型安全を「保証」するようなシステムだと、窮屈に感じるのも Lisperの性。上に挙げたような緊急時対応の場合、部分的に矛盾を生じ得る 定義を送り込む場合もあり得るので (インクリメンタルにパッチを当てる場合とか)。

一応、大抵の処理系ならエラー発生時にデバッガに入り、おかしなデータを その場で書き換えて実行を続行することが可能になってます。でもさすがにこれは 緊急対応ですし、手元で本番稼働してるサーバならともかくユーザにdeliver しちゃったバイナリでデバッガに入られてもどうしようもないですからねえ。

関数と変数の名前空間/Schemeのモジュール

ここはCLerとSchemerが何十年も議論してる話です。Schemerな私としては 分かれた名前空間とか#'とかfuncallとかは嫌ですねえ。CLerにはCLerの言い分がありますが ("list"っていう変数名使いたくならない? とか)。

Schemeのモジュールについては正式にはR6RSをお待ちあれ。 まだ揉めそうですが。

Proprietaryな処理系

これはちょっと複雑な気持ち。 確かに、処理系を売る商売はどんどん苦しくなってるし、 オープンにしてユーザベースを広げ、サービスで喰ってゆく、という方が これからの時代には合ってるかもしれませんが、 現在ある商用Lisp処理系の開発環境が、最初からオープンで開発していたら 得られていたかどうか、ってとこはちと疑問です。 (「今」なら出来るかもしれません。模倣すべき開発環境はあるし、 サービスにチャージするスキームも広く受け入れられてきましたから。 1980年代〜90年代にどうだったか、というのがここでは問題です)。

gccでほとんどの用途に足りるとしても、プロジェクトによっては Intelのiccが「どうしても」必要なことってあるわけです。 個人的にはopenの方が好みですが、商用ベンダが生き残ることの方がもっと 重要だと思うので、生き残りのためにproprietaryになってるなら 仕方ないかなと。もっとも今後、方針の転換を迫られる可能性も 大いにあるわけですが。

それ{Ruby|Erlang|...}でもできるよ

いや実際、かつてLispの独壇場であった領域は、どんどん他の動的言語によって 埋められつつありますな。

個人的には、それは悪いことではないと思います。そういう問題領域に より多くの人の手によって解決が持ち込まれるってことですから。

Lispの人気をどうにかしようという話が出る度に、Lispを追い越して ポピュラーになって行った言語達の持つ機能をLispもコピーしよう、 という話が出ます。ライブラリだの、IDEだの。それはそれで大事だし、 地味に続けないとならないことでしょう。

けれど、それと同じくらい重要なのは、Lispでなければ出来ない領域を もっと広げること、今まで解けるなんて思われていなかった問題を 解けるような言語へとLispを進化させてゆくことではなかろうか、 と思っています。

まあ、どの言語デザイナも似たようなことを考えてるでしょうけど。

EmacsLisp

仕方無いとはいえ、ちょっと歴史の重荷になってきた感はありますね。 いくつかalternativeなプロジェクトがありますが、これまでの Emacs Lispの膨大な資産を引き継げ、さらにEmacsほどに枯れるには まだまだ時間がかかるでしょう。

自由すぎることの意味

Lisp系言語が、プログラマに大きな力を与えるという意味で「自由である」って ところにはあまり議論はありませんね。議論はその有り余る自由が利点か欠点かって ことでしょう。

Lisperの立場から言えば、その自由は、限界に当たった時にその限界をハックして さらにその先へ進むためにどうしても必要なことなんです。ハックだから、 それが恒久的解決になるわけじゃない。正しい解決は処理系の改善や、もしかすると コンピュータサイエンスの進歩を待たなくちゃならないかもしれない。 でも、「今、とりあえず」自分を引き止めている限界を乗り越えたい、 と思ってしまうわけですね。

そう、Lisperはせっかちなんです。

関連

Lisp:よくある誤解, Lisp:Geometry, Lisp:S式の理由

議論、コメント

とても面白く読ませてもらいました。特にToo Dynamic。 仕様はぜんぜん確定していないけど「今」動くものがどうしてもいるので、 そふとうぇあえんじにありんぐに対して後ろめたく思いながらLispを使ってます。


Last modified : 2012/02/23 03:32:47 UTC