マクロと関数の識別

初めて(set! (car p) 3)を見た時、違和感を覚えた人は多いと思う。(car p) は p の先頭部が指し示す値を返すのに、set! の引数に渡すと p 自体が書き変わってしまう。僕も最初に見た時は、引数の評価について何か勘違いをしていたんだと思った。

gosh> (define p (cons 1 2))
p
gosh> p
(1 . 2)
gosh> (car p) 
1
gosh> (set! (car p) 3)
#
gosh> p
(3 . 2)

調べてみると、set! は単にマクロなのでこんな掟破りの動作ができていたんだ。

http://karetta.jp/book-node/gauche-hacks/007244

Schemeの一般化set!は、
(set! (foo expr ...) val)
と書かれた式を、
((setter foo) expr ... val)
と変換することによって動作します。

結局、少し混乱したが納得はできた。でもよく考えると、これは経験を積もうとも新しいコードを読む限り常に出てくる問題じゃないだろうか。

つまり、一見しただけではマクロと関数が識別しにくいんだ。呼び出し側はリストの先頭にあるものをブラックボックスとして呼び出すのではなくて、関数なのかマクロなのか意識しないと動作の正しい理解が得られない。これはコードの可読性を下げていると思う。だって、リストの先頭にあるものがマクロなのか関数なのかによって引数の評価に対する解釈を変えないといけないのに、その答えは定義を見ないと分からないんだ。審判と自分のチームが同じ色のユニフォームを着ているサッカーみたいだ。全員の顔を覚えていないとヘマをしてしまう。例えば、 macroexpand-1 は関数なんだけど、僕は名前からかてっきりマクロだと思って、(macroexpand-1 (macro arg1)) と呼び出した。ちょっとしそれが関数なので、引数はクオートしてやらないといけないのに気付いた。

ここは破壊的操作の名前に!をつけるように、マクロ名には慣習をつけるのがいいと思う。または賢いエディタが関数とマクロを別の色にしてくれるとか。マクロがマクロとすぐに分かれば、その引数は何か特別な解釈が必要だと身構える事ができる。まぁ、実際は一つ一つ覚えるしかないのだろうけど。