Common Lispのポータビリティとユニバーサリティ

clfreaksの収録時に他のLisperと話してて価値観というか目標を共有出来てないなと思ったのでここで心情を語る次第。今のCommon Lispの使われ方には問題がある。

Common Lispにはポータビリティというかユニバーサリティというか、そういうものがない。いや、処理系の作者達はしっかり作ってるのだけどLisperがそういう使い方をしていない。

例えば、Rubyで出来たアプリケーションを使いたいとしよう。Jekyllがいいかな。大抵のシステムには入ってるだろうが一応処理系のインストールから始めてみる。

  • aptなりpkgなりyumなりで処理系をインストールする。それが気に入らなければソースからインストールしてもいい。
  • 処理系をインストールするとgemがついてくるのでgem install jekyllを叩く。
  • あとはJekyllを使うだけ。

あるいは、Octopressなら処理系のインストールのあとは

  • git cloneしてくる。
  • gem install bundlerでbundlerをインストールする。
  • bundle installで依存gemをインストールする。
  • bundle exec rake previewでサーバーが起動する。
  • 止めたければCtrl-Cで止まる。

Common Lispで出来たアプリケーションを使いたいとしよう。cliki。がいいかな。

  • 処理系のインストールはRubyと同じくパッケージマネージャで入る。あるいはソースからインストールしてもいい。この際処理系選びは既に終わっているとする。
  • clikiをgit cloneしてくる。
  • ASDFに読んでもらうために~/common-lisp以下にclikiを移動する。
  • 依存パッケージのインストールのためにquicklispをインストールする。
    • quicklisp.lispをダウンロードしてくる。
    • REPLを起動する。どうやって?処理系依存だから自分で調べろ。
    • Lispの式をいくつか評価してquicklispをインストールする。~/quicklispが出現するがこの際目を瞑る。
  • REPLで(ql:quickload :cliki)を評価して依存パッケージをインストールする
  • (start-cliki-server port homedir wikis)でサーバーが起動する。REPLも起動しっぱなし。
  • 止めたければ
    • Ctrl-Cでinteractive-interruptコンディションを発生させる。デバッガが起動する。
    • デバッガからREPLのtoplevelに戻る。デバッガの操作?処理系依存だから自分で調べろ。
    • REPLを終了する。どうやって?処理系依存だから自分で調べろ。

「アプリケーションは~/common-lispじゃなくて/var/wwwに置きたいんだけど」 - 「コンフィグ書いたら変えられるよ。ASDFのコンフィグ書いてね。コンフィグの書き方はLisperの常識だよね」
「~/quicklisp邪魔なんだけど」 - 「ああ、それも変えられるよ。好きな場所に移動して処理系の初期化ファイル書き直してね。初期化ファイル?処理系依存だから自分で調べろ。どう直すか?簡単なLispの式だから自分で直せるよね?」

多分言いたいことは伝わったかと思う。コマンドラインから扱えないとかパッケージマネージャが我が儘とか色々あるんだけど全ては「Lisperのためだけのものになっている」の一言で表せられる。

quicklispが我が儘なのはまだ許せる。インストール先のディレクトリが固定されてないと管理しづらいから。しかしASDFがライブラリじゃなくてアプリケーションにまで特定のパスにあることを要求してくるのは納得がいかない。コンパイルやらロードやらテストやらの機能があるんだから.asdファイルのあるディレクトリをルートとしてそこだけで完結してほしい。以前、こんな記事を書いた所為でASDFの信者と思われているみたいだが、どっちかというとASDFの方が嫌いだ。ASDFが憖っか依存関係の解決までするがためにアプリケーションの置き場まで制約を受ける。手を広げたがために本来の機能が使い辛くなっている。Lisperが制約を受けるのはまだいい。アプリケーションのユーザにまで制約が及ぶのは耐えられない。

コマンドラインから扱えない問題は多分ノウハウがなかったからだと思う。以前の記事で紹介した方法は多分知られていなかった。あとは複雑で使い辛いと評判(だった)のcl-launchか。アプリケーションはおろかquicklispすらREPLに入らないと使えない。ユーザがLispを微塵も知らない可能性すらあるのに。さらに、コマンドラインから使えないということは他のUnixツール群と組み合わせることも困難になる。また、ここにあるようにREPLでアプリケーションを起動していると問題もある。

私はこの問題を解決するためにCIMを作った。clfreaksの時にも喋ったがCIMのメインの機能はclコマンドとqlコマンドだ。cimコマンドはただの付加価値のためにつけたおまけである。

もしclコマンドが広く使われるようになればshebangにclを使うだけで実行可能ファイルが作れて、コマンドラインから使えるようになる。qlを使えばREPLに入らずにパッケージをインストール出来る。スクリプトがエラーを出してもデバッガに入るようなことはない。Ctrl-Cでちゃんと止まる。

これで一部ユニバーサリティの問題は解決出来たんだけだまだまだ未解決問題がある。

  1. CIMのインストールが必要になる。
  2. コマンドラインツールは解決したにしてもディレクトリ丸ごと持ってくるようなアプリケーションはASDFの制約をうける
  3. コンパイルの問題

1はやっぱりLispを知らない人に使ってもらいたいならCIMのインストールは省きたい。処理系とquicklispだけ入れたら使えてほしい。やっぱりquicklispに変わってもらうしかないのかな。配布物に実行可能形式を含めてたらPATHも通して欲しいし。

2のASDFの制約の問題はASDFを環境変数で制御出来るようなのでうまいことする方法を考えている。良い方法があったらまたアウトプットする。

3は現状CIMで扱いかねてる問題。マクロ展開の問題からコンパイルするのが望ましいんだけどコンパイル後のファイルに互換性がない。しかも互換性がないのに同じ拡張子を使う。そこを上手く扱ってくれるのはASDFなんだけどやっぱり奴は我が儘だし何よりコンパイルしてしまうとAllegroを除いてshebangが効かなくなる。

解決案はいくつか上がってるんだけどまだ決定的なものはない。

  1. コンパイルをあきらめる
  2. 本体だけASDFでコンパイルしてエントリポイントになるスクリプトはコンパイルしない
  3. スタンドアロンバイナリ配布する
  4. ECLを使ってCのファイルを配布する
  5. ユーザー側にスタンドアロンバイナリを作らせる
  6. 特定の処理系を要求する

1は下の策。

2は個人的に推したいところだけどやっぱりASDFが憎い。

3はクロスコンパイルが壊滅的なCommon Lispでは現実的ではない。

4はCommon Lispの利点の一つにSBCLやCCLやCMU CLの速度が速いというのがあるから出来れば自由に処理系を選ばせたい。あとECLのランタイムのインストールが必要になる。

5はやっぱりASDFが憎い。それに依存ライブラリもロードするにはquicklispもロードする必要があって、以前の記事の通りバイナリに邪魔なものが入るしそうでなくてもバイナリが素で60MBとかになる。stripするとアプリケーションとして動かなくなるからstripも出来ない。

6はコンパイル後のファイルがポータブルな処理系はABCL、CLISP、XCLあたりだが、起動の遅いABCL、微妙に仕様に従ってなくて開発も停滞気味なCLISP、既に開発されていない上にマイナーでライブラリのサポートも薄いXCLとあまり選びたくないものばかり。

因みに非Lispユーザにも使われているCommon LispアプリケーションにStumpWMがあるが、5の方法を採用している。しかしASDFを使わずにMakefileでコンパイルしている。依存ライブラリも手でインストールする必要がある。出来ればCommon Lispのツールチェーンに載せたまま配布したいところ…

研究が必要。

Written by κeen