スポンサーリンク

2016年10月7日金曜日

Emacs 25.1 で起動時に警告が

Emacs 25.1 を使ってみた

Emacs 25.1 がリリースされたので、早速使ってみました。24.5 の時の設定は、何も変更せずにほぼそのまま使えました。

一点だけ問題があり、起動時に以下の警告が出るようになってしまいました。

Warning (bytecomp): (lambda (arg) ...) quoted with ' rather than with #'

警告なので内容からしても無視してして構わないと思いますが、起動する度にこれが出るのは精神衛生上よくありません。

lambda 関数のクォートに #' ではなくて、' が使われていることに対して警告を出しているようです。


変数、関数のクォート

elisp のクォートに関して少しだけ説明をしておきます。elisp では変数や関数を参照する時に式を評価させないようにする場合クォートを使います。

変数をクォートするには quote 関数を使います。例えば変数 foo をクォートするには (quote foo) としますが、これは省略形が使え、通常は 'foo と書きます。

また関数をクォートする場合は fuction 関数を使います。例えば my-func という関数をクォートするには (function my-func) と書きますが、これも省略形で #'my-func と書くことができます。

実は quote と function は全く同じ実装で、実行時には何の違いもありません。なので関数をクォートするのに 'my-func と書いても何の問題もありません。実際多くのコードでそのように書いています。

ではこれらの違いは何なのでしょう?それは lambda 関数(匿名関数)をバイトコンパイルする場合に違いが出ます。

'(lambda ...) と書いた場合、バイトコンパイラはその中身が単なるリストなのか関数なのか判断できません。関数であれば更にその中身もバイトコンパイルした方が高速になります。(コンパイルしなくても実行は可能です。) なので、明示的に #'(lambda ...) と書くことにより、バイトコンパイラにこれは関数だと伝えることができます。

更に lambda 関数の場合、#'(lambda ...) の #' を省略することができ、単に (lambda ...) と書いてもまったく同じことになります。つまり結局、lambda 関数はクォートする必要が無いのです。あー、ややこし。

問題点を調査

さて、件の Emacs 25.1 で警告が出る問題の原因を調査したところ、警告が出るのは usage-memo.el パッケージの umemo-initialize() 関数の中でした。
(defun umemo-initialize ()
  "A bunch of `define-usage-memo' definitions. Feel free to redefine!"
  (define-usage-memo ri      "ruby" 0 "ri `%s'")
  (define-usage-memo lh-refe "ruby" 0 "refe \"%s\"")
  ;; slime-describe-symbol, slime-describe-function, and slime-documentation
  ;; calls slime-show-description.
  (define-usage-memo slime-show-description "cl" 0 "*SLIME Description*" umemo-make-entry-name:slime)
  (define-usage-memo describe-function "elisp" 0 "*Help*")
  (define-usage-memo describe-variable "elisp" 0 "*Help*")
  (define-usage-memo describe-mode "elisp" 0 "*Help*"
    (lambda (arg) major-mode)))

この中の一番最後の (define-usage-memo describe-mode ...) の部分で警告が出ます。これだけ見ると、一見 lambda 式は問題無いように見えます。

しかしよく調べてみると、define-usage-memo は関数ではなく、マクロであることに気が付きました。なのでマクロ展開したコードに問題がある筈です。define-usage-memo マクロの中を見ると以下のコードが見つかります。
  (let ((converter (or name-converter-function 'identity)))
    `(defadvice ,command (around usage-memo activate)
       ad-do-it
       (let* ((buffer-fmt ,buffer-fmt)
              (entry-name (funcall ',converter (format "%s" (ad-get-arg ,nth-arg))))
              (buf (with-no-warnings (format ,buffer-fmt entry-name))))
         (umemo-setup ,category entry-name buf)))))

上の lambda 関数は、このコードの中で converter という変数に代入され、最終的に以下のコードで実行されます。
 (funcall ',converter (format "%s" (ad-get-arg ,nth-arg)))

ありました、シングルクォートによる関数のクォート。これを以下の様に変更します。
 (funcall #',converter (format "%s" (ad-get-arg ,nth-arg)))

これで警告が出なくなりました。

Emacs 25 になって文法チェックがより厳密になったようです。今のところこれ以外で問題は出ていません。なのでこのまま 25.1 を使っていこうと思います。お疲れ様でした 24.5。

0 件のコメント :

コメントを投稿