when-required マクロ

Emacs の設定を複数のマシンの共有するときに面倒なのが、require しているパッケージがあったりなかったりすること。例えば、slime を require している .emacs をマシン A とマシン B で共有しているとする。マシン A には slime があり、マシン B には slime がないと、マシン B では .emacs の読み込みに失敗してしまう。

これを軽減するひとつの策として、require を必ず成功させるという方法がある。require には 3 つまで引数を渡すことができて、第 3 引数に t を渡すと、require に失敗しても error を吐かないようになる。

(require 'slime-autoloads nil t)
(slime-setup)

これを色んなところに書くのはあんまりなので、以下の require-safe 関数で実現する。

(defun require-safe (pkg)
  (let ((val (require pkg nil t)))
    (if val
        val
      (progn (warn (concat "failed to require: " (symbol-name pkg)))
             nil))))

こんな風に使う。

(require-safe 'navi2ch)

(when (require-safe 'slime-autoloads)
  (slime-setup))

when と組み合わせることで、require が成功したときだけ何かやるってことが実現できる。この when と require-safe の組み合わせは頻発するので、マクロにしたのが when-required マクロ。

(defmacro when-required (pkg &rest body)
  `(when (require-safe ,pkg)
     ,@body))

when-required を使うと、先の slime-autoloads の部分は以下のようになる。

(when-required 'slime-autoloads
               (slime-setup))

このままだとインデントが妙なので、when-required のインデント設定を変えておく。

(put 'when-required 'lisp-indent-function 1)

これで、

(when-required 'slime-autoloads
  (slime-setup))

こうなる。

require に失敗したときには、*Warnings* にその旨を出力するだけ。起動時に毎回 *Warnings* バッファが立ち上がるのは、うざいと言えばうざいのだけど、初期化が中断して中途半端な状態で立ちあがるよりはいいかなと思う。

追記

コメントで、defmacro の中で (declare (indent 1)) すれば、インデントの設定はいらないという指摘を受けた。いい感じなのでありがたく使わせてもらいます。以下、修正版。

(defmacro when-required (pkg &rest body)
  (declare (indent 1))
  `(when (require-safe ,pkg)
     ,@body))

; これはもういらないので削除する
; (put 'when-required 'lisp-indent-function 1)