2015-02-10

EmacsLisp でマクロを使う (初級編)

EmacsLisp のマクロは byte-compile することで、ソースコードに埋めこまれる。マクロは defun の代わりに defmacro を使って書く。シンプルなマクロは、関数を書くのと変わらない。一点注意するとしたら、マクロは使われる前に定義しておくこと。

マクロの特徴の一つに、byte-compile 時に式を評価できることが挙げられる。一例として deprecated (廃止された) 関数の呼び出しを書いてみる。

関数を使う

Emacs は進化とともに廃止される関数がある。ここでは interactive-p という関数を取り上げてみよう。この関数は、関数がコマンドとして呼ばれた時は t を返し、関数の中から呼ばれた時は nil を返す。しかし、Emacs 23.2 で called-interactively-p という関数に置き換わった。

古い Emacs でも動作を保証するために、関数 called-interactively-p が存在する時はこれを使い、存在しないなら古くからある interactive-p を使う関数 foo-called-interactively-p を作る。自作の EmacsLisp では called-interactively-pinteractive-p を直接呼ばず、 foo-called-interactively-p を使う。

(defun foo-called-interactively-p ()
  (cond
   ((fboundp 'called-interactively-p) (called-interactively-p 'any))
   (t (interactive-p))))

この関数には気に入らない点が二つある。

  1. Emacs 23.2 以降でソースコードを byte-compile すると、 interactive-p は deprecated だと警告が出る
  2. foo-called-interactively-p を呼び出す度に、関数 called-interactively-p が存在するかチェックする

マクロを使う

foo-called-interactively-p をマクロで書き直してみる。マクロを使うと、byte-compile 時に評価が出来るので、 called-interactively-p が存在するか否かのチェックは byte-compile 時に行なえる。

また、マクロによって展開されたコードに (Emacs が 23.2 以降なら) interactive-p は含まれていないので、警告も出ない。Emacs の byte-compiler が出す「本当の警告」に集中することができる。

マクロを使って書き直したコードは次のようになる:

(defmacro foo-called-interactively-p ()
  (cond
   ((fboundp 'called-interactively-p) '(called-interactively-p 'any))
   (t '(interactive-p))))

byte-compile 時に評価するコードはそのまま書いて、残すコードを quote している。

試しに macroexpand を使って、このマクロを展開してみる (Emacs 24.4 にて評価)。結果は次の通り:

(macroexpand '(foo-called-interactively-p))
-> (called-interactively-p (quote any))

called-interactively-p を使うコードだけが残った。

あとがき

EmacsLisp では、C 言語より高度なマクロを書くことができる。使いすぎは毒だけど、適度に使えばとても便利。バッククォートと組み合わせると、更に便利になる。何か良い例題があったら中級編も書いてみたい。

ちなみに、今回紹介したコードは fcopy.el で使っている。

No comments:

Post a Comment