2011-09-23

defpackageのニックネームを指定しないよう再考を

defpackageでは:nicknamesでパッケージの別名を指定できる。
でまあ、ニックネームというぐらいだから短い名前を指定してみたり、何もしなかったりと特に考えたことは無かった。
そして今日始めてニックネームが衝突した。どうやって解決すればいいんでしょう。元のソースはQuicklispでダウンロードしたものなので、それを書き換えるのは無しの方向で。
具体的にはbordeaux-threadsとbinary-typesでどっちもニックネームに:BTを使ってる。

せっかくパッケージ機能で名前空間の衝突の可能性を下げたにもかかわらず、ニックネームに短い名前を書いちゃうことでニックネームの名前空間の衝突が起きてるというバカな事態だと思った。
こんなまぬけな機能がなぜあるんだろうか。

じゃあどうすればいいのか。
まず、パッケージ機能の趣旨からしてdefpackageに指定するパッケージ名はグローバルにユニークになるように努力する。
そして、defpackageの:nicknames機能は封印して使わない。
そうすると利用者は:useでパッケージをまるごとインポートするか、グローバルユニークな(おそらく長ったらしい)パッケージ名修飾されたシンボルを使うかせざるを得なくなる。

そこで、ニックネームを自由に設定できる機能をAPIとして用意してあげればいい。
こうしてみる。
(defun nickname-package (&optional (nickname :tst))
  (rename-package :this.is.a.test
                  (package-name :this.is.a.test)
                  (adjoin nickname (package-nicknames :this.is.a.test)
                          :test #'string-equal)))
 自分のオリジナルの思い付きではない。Climbから拝借した。
これで利用者は
(this.is.a.test:my-function)
と書かなければならなかったところを
(this.is.a.test:nickname-package)
と一度どこかで呼び出せば以降は
(tst:my-function)
と短いパッケージ修飾で書けるので:useでインポートしなくても使えるようになる。
もし:tstが衝突する場合は他の衝突しない名前をnickname-packageの引数として利用者側のコードで指定できるのでライブラリのコードを書き換える必要がない。


もう一点はパッケージ名をグローバルユニークにする方法について。
Java式にドメイン名を逆にするのが簡単で無難なんだろうけど、たとえばメインで開発してる組織の所属が変更になったらどうするんでしょ、って思う。
Sunが消滅した後もcom.sun.プレフィックスのパッケージはそのまま?それともリネーム?とかいう問題。

そこでPerlとかGaucheとかまわりをキョロキョロしてみる。
するとライブラリの提供する機能のカテゴリで分類して階層にするのがベターなんじゃないかと考えた。
たとえばHTTPに関するライブラリだったら、:net.rfc.http とか。
これなら開発母体に変化があっても、ライブラリ機能そのものはそうそう変化しないだろうから、将来的にも破綻しにくい。
かつ、この機能分類の方法に言語コミュニティでコンセンサスができてれば利用者側もライブラリを検索しやく、作成者側もグローバルユニークにしやすい。


自分の書くコードについては少なくともそうしてみようか、と思った。
ていうか、Common Lispでの名前衝突回避のまっとうな解決方法ってどんなもんなのか知りたい。

0 件のコメント: