もっとオブジェクトっぽく書きたい!

オブジェクトっぽく書きたい! - 象徴ヶ淵は実際のところ使っていなかった。
ちょっと問題点があるのである。

(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)))))))))

素晴らしい。まだ表記は胡散臭いところがあるものの、通常より書きやすそうな雰囲気になっている。
マクロ内部の解釈の仕方は他に迷惑を掛けるわけではないので、実装の仕方を変えたくなったら表記はそのままにして、展開方法を変えても良いだろう。