SICP のディジタル回路のシミュレータってのを common lisp 上にポーティングしようと試みている。まずは make-wire からだ。LoL を読んだ直後だったので dlambda が使えると直感的に思い試して見る。
(let ((signal-value) (action-procedures)) (dlambda (:get-signal () signal-value) (:set-signal (new-value) (if (not (eq signal-value new-value)) (progn (setf signal-value new-value) (call-each action-procedures)) 'done)) (:add-action (proc) (setf action-procedures (cons proc action-procedures)) (proc))))
参考までに書くと(誰の参考にもならないだろうが)私の環境は FreeBSD + vi + clisp で、vi で編集して ctrl-z で中断して clisp を起動して実行し ctrl-z で中断して、、、というなかなかオールドスタイル。そこまでするなら screen で切り替えろという気もする。
さて、上の式は簡単に出来た。setf と symbol-function で簡単に名前も付けられた。これを簡便にするには、、、、最初のアイデアが関数で返すことだったのだが、scheme のように define で関数を定義できない。scheme は関数と変数の区別が無いのでそういうことができるのだが、common lisp は出来ない。その代わりといったらなんだがマクロがある。
(defmacro make-wire (var) (setf (symbol-function (quote ,var)) ... let による関数定義が続く
これはすごい。scheme では関数と変数が同じなので make-wire も
(define a (make-wire))
だ。これはscheme として不自然ではない。そしてこれは scheme という枠組みの延長にある。しかし、common lisp で上のマクロを使うと (make-wire a) だ。完全に lisp の中に溶け込んで新しい構文を提供している。一体全体どういうことなのだろう?
C で言えば
my_if (a < b < c) { } my_else { }
みたいな新たな構文が定義できるような感じだ。C++ ではオペレータの over write により C++ の構文拡張が出来るがそれの上を行く。上じゃないかもしれないけど、次元が違うことは確か。C++ で define やら template を屈指して(とあるプログラムを)書くと
//------------------------------------------------------------------------ CXXR_BEGIN { CXXR(A, A) CXXR(A, D) CXXR(D, A) CXXR(D, D) } CXXR_END CXXXR_BEGIN { CXXXR(A, A, A) CXXXR(A, A, D) CXXXR(A, D, A) CXXXR(A, D, D) CXXXR(D, A, A) CXXXR(D, A, D) CXXXR(D, D, A) CXXXR(D, D, D) } CXXXR_END
と書いたことがあるが、もはや C++ じゃない。lisp は言語拡張できて、それでいてまだ lisp だ。逆に言うとそれだけ原始的といえるかもしれない。与えられた自由度が大きすぎるので使う人を選ぶ。誰もが猛獣使いにはなれないということだ。