Common Lispでローカル定数の構文 — #:g1

Posted 2017-05-15 16:31:37 GMT

C#にもローカル定数の構文が導入されるとのことだが、Common Lispにも欲しいという声を目にしたので、ちょっと試しに作ってみた。

(defmacro const (var)
  `((lambda () ,var)))

(defmacro ket ((&rest binds) &body body) (loop :for (var val) :in binds :for gvar := (gensym (string var)) :collect `(,gvar ,val) :into gs :collect `(,var (const ,gvar)) :into cs :finally (return `(let (,@gs) (symbol-macrolet (,@cs) ,@body)))))

const構文にあまり意味はなく、直接lambdaを書いてしまっても良いが、気分的に定義してみた。

(defun fib (n)
  (declare (optimize (speed 3) (safety 0) (debug 0) (hcl:fixnum-safety 0))
           (type fixnum n))
  (ket ((n n))
    (if (< n 2)
        n
        (+ (fib (1- n))
           (fib (- n 2))))))

こんな感じに書いてもコンパイラが最適化してくれるので、ketは無かったことになることが多いだろう(少なくともLispWorksではそうなる)

(defun fib (n)
  (declare (optimize (speed 3) (safety 0) (debug 0) (hcl:fixnum-safety 0))
           (type fixnum n))
  (ket ((n n))
    (if (< n 2)
        n
        (+ (fib (decf n))
           (fib (decf n))))))

こういうのはマクロ展開時にエラーになる。

このketは、定数を宣言しているのではなく、setfsetqでエラーを起すようにしたもの。

(let ((x 42))
  (setf (const x) 42))

でエラーになるようにしたと考えれば判り易いだろう。
それ故、setfsetq時のエラー内容は全く定数云々の件とは異なるので、この辺りに手抜き感が漂う。

defconstantを使うものに展開するという手もあるが、defconstantはトップレベルに置かないと上手く機能せず、そこをコードウォークでやりくりするにしても色々面倒なので、まあこの辺りで手を打ってみた。

ちなみに、ローカルな定数構文の提案は、Common Lispの仕様策定時にはあり、1987年にlet-constant/constantletという代物が提案されている。
(declare (constant foo))のような宣言も提案されていたようだが、しかし、紆余曲折でどこかに行ってしまったようだ。

結び

やっぱりコンパイラで対応してくれないときびしい。


HTML generated by 3bmd in LispWorks 7.0.0

comments powered by Disqus