symbol-macrolet 内で macrolet したら(sbcl にて)

(symbol-macrolet ((foo (+ 1 2 3)))
  (macrolet ((foo () `,foo))
    (list foo (foo))))
=> (6 30)

(symbol-macrolet ((foo :symbol-macro))
  (macrolet ((foo (foo) `(list ,foo foo)))
    (foo :local-macro-arg)))
=> (:LOCAL-MACRO-ARG :SYMBOL-MACRO)

ふむ

(symbol-macrolet ((foo :SYMBOL-MACRO))
  (macrolet ((bar (arg) `(list ,foo ,arg)))
    (bar :MACRO-ARG)))
=> (:SYMBOL-MACRO :MACRO-ARG)

(symbol-macrolet ((foo :SYMBOL-MACRO))
  (macrolet ((bar (arg) `(list foo ,arg)))
    (bar :MACRO-ARG)))
=> (:SYMBOL-MACRO :MACRO-ARG)

symbol-macro foo は macro bar の展開時でも展開後でも expansion :SYMBOL-MACRO に展開される、と。

;; これは xyzzy
(macroexpand '`(list foo ,arg))
=> (list 'list 'foo arg)

(macroexpand '`(list ,foo ,arg))
=> (list 'list foo arg)

えーと、macrolet のマクロ定義部分で symbol-macrolet を展開しても 'foo は symbol-macro 展開しないのでそのままになる。でも macro 展開されると foo になって symbol-macro 展開はしなきゃダメって話に。

(defmacro get-env (&environment env)
  `,env)
=> GET-ENV

(macrolet ((foo () :EXPANSION))
  (macroexpand '(foo) (get-env)))
=> :EXPANSION
=> T

(symbol-macrolet ((foo :SM-FOO))
  (macrolet ((foo (foo) `(list ,foo foo)))
    (macroexpand '(foo :ARG) (get-env))))
=> (LIST :ARG FOO)
=> T

(symbol-macrolet ((foo :SYMBOL-MACRO))
  (macrolet ((foo (foo) `(list ,foo foo)))
    (foo :MACRO-ARG)))
=> (:MACRO-ARG :SYMBOL-MACRO)

(少なくとも sbcl では)先に macrolet で定義されたローカルマクロを展開して、その後(ローカルマクロの展開形)に含まれる symbol-macro を展開するようだ。

(macrolet ((NAME ARG MACRO-DEF) ...) BODY...)
  • MACRO-DEF 部分では symbol-macro 有効、ARG が shadowing される
  • BODY 部分はふつーに symbol-macro 有効
  1. MACRO-DEF 内の symbol-macrolet を展開
  2. BODY 内の ローカルマクロ NAME 呼び出しを展開
  3. expansion に含まれる symbol-macro が展開

ふーむ・・・。