Posted 2021-12-23 14:35:02 GMT
Lisp一人 Advent Calendar 2021 23日目の記事です。
SBCLではコンパイラマクロの発展形のようなdeftransform
でコンパイル時の型情報を扱って式の展開を制御できたりします。
下記は、x+y
という2引数の関数の型情報をみてコンパイル時に式を展開する例です。
#+sbcl
(sb-c:defknown x+y (t t) t () :overwrite-fndb-silently t)(sb-c:deftransform x+y ((x y) (cl:number cl:number))
'(cl:+ x y))
(sb-c:deftransform x+y ((x y) (cl:list cl:list))
'(cl:append x y))
(sb-c:deftransform x+y ((x y) (cl:string cl:string))
'(cl:concatenate 'cl:string x y))
SBCLでしか使えないのが残念ですが、しかし、良く考えてみると、型の情報は取得できなくても、変数の型宣言の情報は、variable-information
で取得できるので、コンパイル時にコンパイラマクロで展開を制御できるのではないかと思ったので、試しに書いてみます。
(import #+sbcl 'sb-cltl2:variable-information
#+lispworks 'hcl:variable-information
#+allegro 'sys:variable-information);;; デフォルトの挙動
(defun foo (x) (list :default x))
(foo 42)
→ (:default 42)
;;; コンパイル時に展開を制御するユーティリティ
(defmacro variable-information-typecase (var env &body clauses)
`(case (cdr (assoc 'type (nth-value 2 (variable-information ,var ,env))))
,@clauses))
;;; foo にコンパイラマクロを設定
(define-compiler-macro foo (&whole w x &environment env)
(variable-information-typecase x env
(fixnum `(list 'fixnum ,x))
(string `(list 'string ,x))
(symbol `(list 'symbol ,x))
(T w)))
コンパイラマクロを設定したので、コンパイルして確認してみます。
(defun fixnum-bar (x)
(declare (type fixnum x))
(foo x))(defun string-bar (x)
(declare (type string x))
(foo x))
(defun symbol-bar (x)
(declare (type symbol x))
(foo x))
(fixnum-bar 42)
→ (fixnum 42)
(string-bar "foo")
→ (string "foo")
(symbol-bar 'foo)
→ (symbol foo)
できました!
型宣言がないと分岐できないものの、宣言を活用すれば、コンパイル時に展開を分岐することは可能みたいです。
ちなみに、Allegroは型の名前がちょっと違うので、上の定義では上手く行きません。
variable-information
自体がCLtL2の拡張なので可搬的ではありませんが、サポートしている処理系はそこそこ多いので活用できるのではないでしょうか。
■
HTML generated by 3bmd in LispWorks 8.0.0