@kyanny's blog

My thoughts, my life. Views/opinions are my own.

app-emacs/color-moccur と app-emacs/moccur-edit を両方 merge すると emacs が起動しない、からちょっと深追い

Emacs で wdired と moccur-edit を使っていない人は(ry - higepon blog で紹介されている color-moccur と moccur-edit という便利な elisp があり、あまり使いこなせてはいなかったのですが今後もっと活用していこうと思い、 /usr/portage 以下を覗いてみたら両方とも ebuild があったので両方とも入れて ~/.emacs.d 以下のものは外して emacs を再起動すると、起動中にエラーが出てしまいました。 moccur-edit を unmerge したら普通に起動するようになったのでとりあえずそれでしのいでいますが、ではなぜエラーになるのか?を emacs lisp の勉強がてらにメモします。

まず、 emacs を --init-debug オプションつきで起動すると詳しいバックトレースが見れます。これによると site-gentoo.el というファイルの読み込み中にエラーになっているらしい。このファイルは Gentoo が用意している app-emacs/* という emacs lisp パッケージ群をまとめてロードするためのファイルで、 ~/.emacs の中で (require 'site-gentoo) として読み込みます。これは gentoo に特有の決まりです。バックトレースは以下のようになりました。

Debugger entered--Lisp error: (void-variable moccur-edit)
  eval(moccur-edit)
  eval-after-load("color-moccur" moccur-edit)
  eval-buffer(#<buffer  *load*<2>> nil "/usr/share/emacs/site-lisp/site-gentoo.el" nil t)  ; Reading at buffer position 8471
  load-with-code-conversion("/usr/share/emacs/site-lisp/site-gentoo.el" "/usr/share/emacs/site-lisp/site-gentoo.el" nil t)
  require(site-gentoo)
  eval-buffer(#<buffer  *load*> nil "/home/kyanny/.emacs" nil t)  ; Reading at buffer position 158
  load-with-code-conversion("/home/kyanny/.emacs" "/home/kyanny/.emacs" t t)
  load("~/.emacs" t t)
  #[nil "^H\205\276^@   \306=\203^Q^@\307^H\310Q\202A^@ \311=\2033^@\312\307\313\314#\203#^@\315\202A^@\312\307\313\316#\203/^@\317\202A^@\31\5$
  command-line()
  normal-top-level()

それで、 site-gentoo.el (/usr/share/emacs/site-lisp/site-gentoo.el にあります) を読んでいくと、まず color-moccur を読み込む部分があり、次に moccur-edit を読み込む部分がありました。 color-moccur のほうがファイルの先頭のほうにあります。それぞれの部分は以下のようになっていました。

;;; color-moccur site-lisp configuration

(add-to-list 'load-path "/usr/share/emacs/site-lisp/color-moccur")
(mapc (function (lambda (x) (autoload x "color-moccur" nil t)))
      '(moccur
        dmoccur
        dired-do-moccur
        Buffer-menu-moccur
        grep-buffers
        search-buffers
        occur-by-moccur
        isearch-moccur
        moccur-grep
        moccur-grep-find))
;;; moccur-edit site-lisp configuration

(add-to-list 'load-path "/usr/share/emacs/site-lisp/moccur-edit")
(eval-after-load "color-moccur"
  (require 'moccur-edit))

ここでいくつかよくわからない関数が出てきたので、 C-h f で調べます。もちろんそれだけでは理解できないので、ぐぐります。ただし、詳しく調べる前にもぼんやりとわかることはあります。

まず、 moccur-edit のほうでは eval-after-load という関数を使っていて、これは color-moccur が定義済みの場合に moccur-edit を require しろ、ということらしい。 moccur-edit は color-moccur がないといけない、ということなので color-moccur のほうが先頭で定義されているわけです。

そして color-moccur のほうを見てみます。 mapc とか lambda とか、 lisp っぽい名前が出てきて腰が引けますが、 autoload となっているのに注目しました。 autoload は必要になったときにはじめてロードする仕組みだったはずなので、 site-gentoo.el が評価される emacs の起動時にはここでは color-moccur のいくつかの関数?は実際にはロードされず、つまり未定義のまま、ということになるはずです。

よって、 color-moccur がない状態で moccur-edit をロードしようとするのでエラーになる、というような不整合が起こっているのかな、と考えて moccur-edit のほうを外してみたところ、エラーがなくなった、というわけです。

ここからさらに深追いをして、 mapc というところで何をしてるのか、解読を試みました。

まず mapc ですが、 mapcar とほぼ同じだが副作用のために使うもので、返り値を使わないときに使う、とあります。 map なんたらなので、 Perl の map と似たように、リストに対して操作を適用するものです。操作というか関数を、です。 http://flex.ee.uec.ac.jp/texi/eljman/eljman_99.html にも詳しい説明があります。で、帰り値のリストが欲しいときに使う mapcar については Emacs Lisp のパターン - あどけない話 に詳しいので、ここをみると、第一引数には関数のシンボルを渡すようです。

次に、 site-gentoo.el の color-moccur をロードする部分のコードでは、 mapc の第一引数には funciton lambda という関数が続いています。この、 function というやつがわからなくて悩みました。ヘルプをみると quote と同じだがバイトコンパイルのときに違いがある、という意味のことが書いてありましたがさっぱり意味がわかりません。評価してみても、 (quote (a)) => (a) で (function (a)) => (a) と変わらずです。

これもぐぐると、 http://flex.ee.uec.ac.jp/texi/eljman/eljman_91.html#SEC91 という説明があり、しかしこれでもまだ意味がわからないのでさらに調べると Emacs Lisp というより詳しい説明がありました。これでぐっと理解がしやすくなりました。 On Lisp にのっていた #' (シャープクォート) がここで出てきているわけです。

つまりこの部分は、 autoload を逐一かくかわりに map 関数を使ってまとめて定義していると。そのときに、 mapc に渡す関数を lambda で無名関数として定義していて、それをバイトコンパイル時に最適化できるように function というものを使っている。愚直にかくと、 (autoload 'moccur "color-moccur" nil t) と並べてかくのとほぼ等価ということです。で、ここでこういう書き方をしたせいで、 mcccur-edit のほうを一緒に入れると動かなくなる、と。でもなんで動かないのかはよくわかりません。 eval-after-load を使っているのになぜなのか・・・。

autoload については 入門 GNU Emacs 第3版 の 243 ページ、 9.2.1 言語モードの項に詳しく書いてありました。やはり、ある関数が必要になったときにもしまだ読み込まれていなければ、 autload による関係を探し、 autoload で定義されているファイルを読み込む、という仕組みのようです。

そういうわけで、実際に emacs lisp のコードを読みながらだと、ブログのエントリやマニュアル、書籍を読むときも真剣さがでてきてよく身につく気がする、という話でした。こういう地味なメモでも、何年か続ければ結構多くのことを網羅できるんじゃないかと思うので、機会があればちょっと深追い、というのを続けていこうと思います。

入門 GNU Emacs 第3版

入門 GNU Emacs 第3版

  • 作者: Debra Cameron,James Elliott,Marc Loy,Eric Raymond,Bill Rosenblatt,宮下尚,半田剣一,新井貴之,鈴木和也
  • 出版社/メーカー: オライリー・ジャパン
  • 発売日: 2007/03/12
  • メディア: 大型本
  • 購入: 14人 クリック: 331回
  • この商品を含むブログ (117件) を見る
On Lisp

On Lisp