FTYPE declaration for generic functions. (Common Lisp)
TLDR
- ある程度最適化したい。
- いちいち
optimize
宣言を各関数に挟みたくない。 - ファイルの先頭でグローバルに
optimize
宣言をdeclaim
で行う。 - SBCLがコンパイラノートをめっちゃ吐く。
- ノートに従うことによりノートを消したい。
- 総称関数の返り値わからん問題によりノートが消せない。
- FTYPE宣言により消せましたよ。
というお話。
CLOS
CLOSは限界まで動的にしようとしているきらいがあります。
すなわち「実行時に」クラスが書き換えられることをも視野に入れていることを意味します。
総称関数もCLOSの一部なためその文脈に従います。
すなわち「実行時に」メソッドが書き換えられることをも視野に入れているということです。
ところが実際に実行時に振る舞いを書き換えたい場合がどれくらいあるかと申しますと、あまりありません。
総称関数を採用する多くの場合、欲しいのは引数の型による振る舞いのディスパッチでしょう。
振る舞いそのものを実行時に変更したいという需要はそう多くはありますまい。
たとえばあるクラスを宣言するとします。
以下のようなものです。
(defclass c ()
((s :type fixnum :reader s)))
クラスC
はスロットS
を持ち、そのスロットはリーダメソッドS
で読み取ります。
さて、問題はリーダS
の返り値です。
スロットS
ではFIXNUM
であると型宣言されています。
S
の返り値は必ずFINXUM
になりそうなものです。
実際そのように振る舞うのですが、コンパイラはこの推測を行えません。
というのもクラスや総称関数は実行時に書き換えられるかもしれないからです。
以下のフォームは最適化できません。
(1+ (s var))
S
の返り値は実行してみない限り分からないからです。
CLOSの思想としてそのような振る舞いを取るのに否やはありません。
されど一般的な需要としてはやはり動的に振る舞いを変えたい場合は少のうございます。
これをなんとかしたい。
どのようなメソッドが追加されたとしても、S
は必ずFIXNUM
を返すのだと宣言したい。
しかしCLOSではそれを実現できない。
FTYPE declaration.
FTYPE
宣言を行うことでこれを実現できるらしい。
「らしい」という曖昧な表現になってしまっているのは、これが仕様では明言されていないからです。
ただし、FUNCTION-NAME
は関数のサブタイプでなければならない(だからマクロには使えない)という一文があることから、総称関数に対して宣言しても問題ないと解釈できるにはできます。
もっともここで言う「サブタイプ」が想定しているのが総称関数であるというのは少々疑わしく、おそらくはCOMPILED-FUNCTION
を指しているのだろうとは思いますが。
以下のように宣言をするとコンパイラはノートを吐かなくなります。
(defun demo (c)
(declare (ftype (function (c) (values fixnum &optional)) s))
(1+ (s c)))
declaim
でもってグローバルに宣言しても良いのですが、その場合再読込時にコンパイラが今度は警告を発するようになります。
この場合の警告は「関数の型が宣言されているが同名のものが総称関数として定義されたので宣言を無効にする」というものです。
DECLARE
によるローカルな宣言の場合そのような警告は出ません。
Conclusion.
本気で最適化したい場合は総称関数を使わないのがベターだとは思います。
そのような場合、以下のライブラリが助けになるやもしれません。
Discussion