もっとオブジェクトっぽく書きたい!
オブジェクトっぽく書きたい! - 象徴ヶ淵は実際のところ使っていなかった。
ちょっと問題点があるのである。
(defmacro radical-macro (var arg) `#[,var car cdr cddr car (setf ,arg)]) | error: ‘,’がバッククオート構文以外に現れました
調子に乗って、これを使ってマクロを書こうとしたが、うまくいかなかったのだ。それで失望して使うのをやめた。
しかし、良く考えてみれば、せっかくリーダマクロを使っているのに、マクロを書くために使うのでは意味がない。
何故こんなことをしようとしたかといえば、まだ表記が冗長だからである。
;これはひどい。 [complex-variable (elt 20) (elt 5) (elt 1) (do-something)] [complex-table (gethash 'hoge) (gethash 'fuga) (gethash 'piyo) (do-something)]
何か対策が必要だ。そこで、既存の構文に無理に対応することを考えるのではなく、内部に別の規則を持つことにした。
(defun \[-reader (stream char) (let* ((*readtable* *\[-readtable*)) (let* ((obj (read-delimited-list #\] stream nil)) (l (car obj))) (dolist (a (cdr obj) l) (setq l (if (consp a) (list* (car a) l (cdr a)) (list a l))))))) (defvar *\[-readtable* (macrolet (($ (c fun &optional both) (if both (let ((/c (gensym)) (/fun (gensym))) `(let ((,/c ,c) (,/fun ,fun)) (set-macro-character ,/c ,/fun nil readtable) (set-macro-character ,/c ,/fun nil old-readtable))) `(set-macro-character ,c ,fun nil readtable))) (with-old (&body body) `(let ((*readtable* old-readtable)) ,@body))) (let ((readtable (copy-readtable)) (old-readtable (copy-readtable nil))) ($ #\] #'(lambda (s c) #\]) t) ($ #\. #'(lambda (s c) (list 'getf (read s)))) ($ #\' #'(lambda (s c) (with-old (list 'elt (read s))))) ($ #\` #'(lambda (s c) (with-old (list 'gethash% (read s))))) ($ #\= #'(lambda (s c) (with-old (list 'setf (read s))))) ($ #\, #'(lambda (s c) (read s))) ($ #\( #'(lambda (s c) (with-old (funcall 'si::\(-reader s c)))) readtable))) (defmacro gethash% (a b) `(gethash ,b ,a)) (set-macro-character #\[ #'\[-reader) ;ディスパッチングマクロにするのをやめた。
すると、表記は次のようになる。
'[hoge = 20] | (setf hoge 20) '[hoge = (make-hash-table) `'foo = 'bar] | (setf (gethash 'foo (setf hoge (make-hash-table))) 'bar) '[hoge`'fuga`'piyo`'foo = 'bar] | (setf (gethash 'foo (gethash 'piyo (gethash 'fuga hoge))) 'bar) `[hoge`1.9] | (gethash 1.9 hoge) ;小数点として認識 `[hoge.1.2.3.4.5.6.7.8.9 = 'foobar] (setf (getf (getf (getf (getf (getf (getf (getf (getf (getf hoge 1) 2) 3) 4) 5) 6) 7) 8) 9) 'foobar) ;小数点にはならない(意味がないので) [hoge = nil] [hoge.1.2.3.4.5.6.7.8.9 = 'foobar] | foobar hoge | (1 (2 (3 (4 (5 (6 (7 (8 (9 foobar)))))))))
素晴らしい。まだ表記は胡散臭いところがあるものの、通常より書きやすそうな雰囲気になっている。
マクロ内部の解釈の仕方は他に迷惑を掛けるわけではないので、実装の仕方を変えたくなったら表記はそのままにして、展開方法を変えても良いだろう。