わかっていなかったのは、マクロの中で呼ぶ関数の振舞いです。
まず、諸処定義。
CL-USER> (setf a 1 b 2)
2
CL-USER> a
1
CL-USER> b
2
CL-USER> (defmacro a-mac (x y)
(a-fun x y))
A-MAC
CL-USER> (defun a-fun (x y)
(+ x y))
A-FUN
CL-USER>
この関数を呼び出したとき、もちろん引数はeagerに評価されます。
CL-USER> (a-fun a b)
3
CL-USER>
あ、この例では、eagerもlazyもへったくれもないですが、まぁCommon Lispの関数呼出しの評価モデルはeagerです。
では、(a-mac a b) ではどうだろう、ということなんですね。a-mac自体はマクロなので、引数を評価しないので、それは(a-fun a b)になる。そしてこのように変換された後に評価されるから、(a-fun a b)は上の関数呼び出しと同じで3になるじゃん、と思ったんですね。これが間違い。エラーです。
CL-USER> (a-mac a b)
; Evaluation aborted.
CL-USER>
エラーはこんな感じ。
`A' is not of the expected type `NUMBER'
[Condition of type TYPE-ERROR]
ちょっと考えてみればアタリマエなんですが、a-macの中のa-fun呼出しはマクロ展開関数の一部ですから、そのときの動作は、通常の関数呼出しの振舞いの定義に従うのではなく、あくまでマクロ展開関数の振舞いの定義に従うのですね。で、マクロ展開関数の中では、そもそもマクロの引数として渡された lisp form は評価されずにそのまま取り扱われるのです。関数がどんなに入れ子になっていてもマクロ展開関数の中ならば、ざっくり言うと、引数は評価されません。そのまま取り扱います。最終的にマクロ展開関数が返す lisp form が初めて評価されるのです(実行時環境に対して)。
ちなみに、上で間違いといった解釈に多少合致したマクロは次のものです。
CL-USER> (defmacro b-mac (x y)
(list 'a-fun x y))
B-MAC
CL-USER> (b-mac a b)
3
CL-USER>
ここでは、マクロ展開関数が(a-fun a b)というlisp formを返します。そのlisp formを評価するときは通常の関数呼出しですから、eagerに(a-fun 1 2)となり、3となります。
Common Lispの関数の引数は常にeagerに評価されると思っていたのが間違い。マクロ展開関数の中で呼出されるときは評価しないんですね!
うひー、恥ずかしい。。。でもよかった。
こつこつ。
0 件のコメント:
コメントを投稿