Edited at
EmacsDay 9

Emacs Lisp で括弧を省略できる式として利用できるシンボル

More than 1 year has passed since last update.

Emacs Adevent Calendar 2016 の 12/9 が空だったため、Emacs Lisp の豆知識を入れます。

Emacs Lispでは、引数を評価しない特殊形式 quote は頻出するため、 ' で代用できます。

すなわち以下の2式は等価で、上側の式は括弧が不要になっています。

'a

(quote a)

このように、括弧を省略できる式として扱えるシンボルが、Emacs Lisp では他に4つ用意されています。

バッククォート(すでに backquoteマクロとして定義済です)

`a

(\` a)

カンマ (バッククォート内で、特殊シンボルとして利用されています)

,a

(\, a)

カンマ・アット(バッククォート内で、特殊シンボルとして利用されています)

,@a

(\,@ a)

カンマ・ドット

,.a

(\,. a)

上記のうち、バッククォート以外は、関数やマクロとして定義されていないため、自分で利用できます。カンマ・ドットは1995年頃、 Common Lisp のバッククォートの「破壊的なスプライシング」と互換性を持たせるために Reader に導入されました。しかし、破壊的なスプライシングそのものが使いにくという理由のため、 backquote.el 本体には実装されず、かといって、Reader からもカンマ・ドットは削除されず、放置状態が続いています。

以下に、これらのシンボルを局所的な関数として利用する例を示します。

(defun example (info)

(concat
"name=" (assoc-default :name info) "\n"
"address=" (assoc-default :address info) "\n"
"tel=" (assoc-default :tel info) "\n"
...))

上記のように引数が1つだけ異なる関数を大量に呼び出す場合、それを cl-flet で以下のように置き換えると

(defun example (info)

(cl-flet ((\, (key) (assoc-default key info))))
(concat
"name=" ,:name "\n"
"address=" ,:address "\n"
"tel=" ,:tel "\n"
...)))

このようにコードをすっきりさせることができます。

コードを変数化し、別に分離する際には、 eval 時に backquote を必要とする場面がしばしばあるため、 backquote マクロに影響されないカンマ・ドットは特に有用です。

(defvar info

'((:name . "hoge") (:address . "page") (:tel . "num")))

;; ここで format を変数として分離したい。
(defvar format
'("name=" ,.:name "\n"
"address=" ,.:address "\n"
"tel=" ,.:tel "\n"))

;; 実際の評価
(defun example (info)
"Test INFO."
(eval `(cl-flet ((\,. (key) (assoc-default key info)))
,(cons 'concat format)) t))