Random Notes

Lisp, Haskell, OCaml, etc

マクロ内でevalするべきでない理由

| Comments

答え:レキシカル環境が無視されるから。

実例で解説しよう。CL-Yaccというライブラリがある。名前の通りyaccをCommon Lispにポートしたものだ。実装は単純明快で、背景となる理論を知っていればすんなり読めるはずだ。

CL-Yaccはdefine-parserというマクロを提供しており、ユーザーはこれを用いてパーサーを定義する。以下に簡単な四則演算パーサーの例を示す。

1
2
3
4
5
6
7
8
9
10
(yacc:define-parser *parser*
  (:start-symbol expression)
  (:terminals (integer + - * /))

  (expression
   (expression + expression (lambda (a _ b) (declare (ignore _)) (+ a b)))
   (expression - expression (lambda (a _ b) (declare (ignore _)) (- a b)))
   (expression * expression (lambda (a _ b) (declare (ignore _)) (* a b)))
   (expression / expression (lambda (a _ b) (declare (ignore _)) (/ a b)))
   integer))

セマンティックアクションがすべて類似していることに気付くだろう。試しにmacroletを使って以下のように書きかえてみる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
(macrolet ((app (op)
             `(lambda (a _ b)
                (declare (ignore _))
                (,op a b))))
  (yacc:define-parser *parser*
    (:start-symbol expression)
    (:terminals (integer + - * /))

    (expression
     (expression + expression (app +))
     (expression - expression (app -))
     (expression * expression (app *))
     (expression / expression (app /))
     integer)))

ネストは深くなったが、パーサー定義はより簡潔になったはずだ。しかし、この新しい定義を評価(コンパイル)するとThe function APP is undefined.というエラーが発生する。なぜか。

CL-Yaccはマクロ展開時にパーサーテーブルを生成する。そのとき、セマンティックアクションも関数形式に変換して同テーブルに格納する。このような手順をとる場合、セマンティックアクションとしてdefine-parserに記述されたフォーム(先の例では(lambda (a _ b) ...))はevalで評価せざるを得ない。

evalに渡されたフォームは空レキシカル環境、つまりグローバル環境上で評価される。空レキシカル環境上で評価されるということは、symbol-macroletmacroletなどのレキシカル環境に作用するオペレータが全く無効になるということである(たとえ字句的に包含していようが)。したがって、上記のAPPは見えなかったというわけである。

この問題は全くもってLispの力を制限するものであるから、本当に必要なときを除いて、特にマクロ展開中は、evalは使わないようにすべきである。

初投稿

| Comments

Octopressでブログを初めることにした。Markdown記法で書けるのが主な理由。テストがてら投稿してみる。

1
2
3
4
5
6
7
8
9
10
(print
 (loop for i from 1 to 100
       if (zerop (mod i 15))
         collect "Fizz Buzz"
       else if (zerop (mod i 3))
         collect "Fizz"
       else if (zerop (mod i 5))
         collect "Buzz"
       else
         collect i))