Package, system, and more.

Introduction.

こういうツイートがありました。

結局roswellとsbclのどっちが正解なの…ってなるのでcommon lisp嫌い〜

実は似たようなツイートを以前にも見た記憶があります。

幸い僕はRoswell以前にCommon Lispの学習を始められたので、asdf、quicklisp、roswellとステップバイステップで悩むことなく学習できたのですが、今からCommon Lispの学習をする人は結構この点で躓いてるみたいです。

という訳でこの辺の階層的に作られてる各種機能について簡単に概要を説明してみます。

Implementation.

Common LispはANSIで仕様が定められています。 仕様に則ったものは全てCommon Lispを名乗れます。

SBCLはそんなCommon Lisp処理系の一つです。

ちょうどCの世界でコンパイラがgccやらllvmやらあるのと似ています。

Before common lisp.

古のLispには名前空間がありませんでした。 これは複数のライブラリに依存するアプリケーションを作る際に問題となることがあります。

ライブラリLが提供するシンボルSとライブラリMが提供するシンボルSとで名前衝突(conflict)が起こりうるのです。

これを解消するためにCommon Lispには名前空間が導入されました。 導入された名前空間はPACKAGEと言います。

Linuxの界隈でいうパッケージとは意味が異なるので注意が必要です。

ASDF as build tools.

名前空間が導入されたことで複数の依存ライブラリを従えるような巨大なアプリケーションの実装が容易になりました。

それはつまりソースコードが肥大していくことを意味します。 その帰結としてファイルを分割したいという需要が出てきます。 しかしファイルが分割されると新たな問題が副作用として起こります。 分割されたファイルには依存関係があり、適した順番でコンパイル、ロードしなければならないという問題です。

Common Lispという言語はこれを解決するための機能を提供していません。 というのもファイルの依存関係の指定手段は複数あるからです。 代表的な方法は依存関係を記したデータファイルを用意し、それをビルドツールに食わせるというものです。 今一方の代表は各ファイルの先頭にそのファイルが依存しているファイルを記し、ビルドツールにはエントリポイントとなるメインファイルを食わせるというものです。

これらの手法は数学的にどちらが正しいと導き出せるものではありません。 言わば趣味の問題です。 趣味の問題でコミュニティ間のコンセンサスを得るのは時間がかかるためでしょうか、Common Lispの言語仕様では最低限のコンパイル・ロードを行う機能のみの提供にとどまっております。

その結果オレオレビルドツールが乱立することとなるのですが、最終的にはASDFが覇権を握ることとなります。

ASDFはCの世界でいうmakeのようなものです。

Quicklisp as distribution.

Common Lispの世界においてビルドツールはASDFがデファクトスタンダードとなりました。

ですがASDFはシステムをビルドすることしかスコープに含みません。 すなわち、アプリケーションがあるライブラリDに依存しているとして、それがすでにローカルにインストールされていることを想定しているのです。 ローカルにインストールされていないライブラリをネットワークからダウンロードしてくるような機能は有しておりません。

その問題を解決するために開発されたのがQUICKLISPです。

Quicklispはlinuxの世界でいうapt-getのようなものです。

Roswell as implementation manager.

Quicklispが登場したことでCommon Lispエコシステム界隈は大きく変わりました。 具体的にはライブラリが小さくなった点があげられます。

Quicklisp以前の世界では自身のアプリケーションを第三者に使ってもらうにはREADMEに書いてあるとおりに手動で各種依存ライブラリをダウンロードしてもらう必要がありました。 そのような状況下でユーザの利便性を考慮すると、なるべく依存ライブラリを少なくし自前で実装してあるほうがよいとの結論に達します。

ですがQuicklisp以降はアプリケーションの本質とは関係ないサブモジュールは可能なかぎり独立したライブラリとして設計し、別途Qcuiklispに登録したほうが良いことになります。

ライブラリが小さく把握が容易になると、それは広く使われるようになります。 広く使われるということは様々な処理系で使われるということでもあります。 ライブラリ開発者がより広く使ってもらえるようにアピールするポイントとして各Common Lisp処理系でポータブルに動くという点があげられます。

開発者は各処理系をローカルにインストールし、自身のライブラリのテストを回す必要があります。

また、ここ昨今Lisp処理系はローリング・リリースを採用するようになってきています。 代表がSBCLで、月一でリリースされています。

ここで問題になってくるのが各Common Lisp処理系のインストール方がまちまちだという点です。 最新版をソースからビルドする場合、当然その手法が各々異なるのです。

それらの問題を解決するために開発されたのがRoswellです。

また、各処理系はシェルから呼び出す際の引数指定方が異なったりもします。 Roswellはそれらにも統一的なインターフェイスを提供することで解決しています。

またQuicklispには、登録されたシステムでないとダウンロードできないという問題があります。 この問題もRoswellは解決しようとしています。 2020/10/18現在github上のシステムならRoswell経由でダウンロードできます。

Conclusion.

見てきたように各機能は直近の問題を解決するために開発されてきています。 それらが今では分厚い階層をなしています。 初心者から見て厄介なのは各機能が下層の機能を隠蔽していないことでしょう。

Roswellさえ知っていればQuicklispについては知らなくても良い、とはなっていないのです。 同様にQuicklispさえ知っていればASDFについて知らなくても良いとはなっていませんし、ASDFさえ知っていればPackageについて知らなくても良いともなっていません。

各々問題の関心事が違うので隠蔽のしようがないというのが実情で、それは見てきた通りです。 逆に各々の関心事さえ把握できれば、各階層が解決しようとしている問題はスコープが狭いので把握は容易であろうかと存じます。