関数にlocalな変数を作る。あとbyte compilerのwarningsを抑える

emacs lispで複数の関数で共有したい変数を作りたい場合、普通は、グローバル変数を作って

(require 'cl) ; incf
(defvar my-count 0)
(defun my-inc-count ()
  (incf my-count))
(defun my-reset-count ()
  (setq my-count 0))
(defun my-get-count ()
  my-count)

の用に書くと思いますが、 cl パッケージの lexical-let マクロを使うとclosureを模倣できるので、グローバル変数を作らずに同じような動作をするコードを書けます。

(lexical-let ((count 0))
  (defun my-inc-count ()
    (incf count))
  (defun my-reset-count ()
    (setq count 0))
  (defun my-get-count ()
    count))

(my-inc-count)
(my-inc-count)
(my-get-count) ;2

(my-reset-count)
(my-get-count) ;0


コードは問題なく動作します。しかし、このコードをバイトコンパイルすると、以下のようなWarningがでてしまいます。

sand.el:30:1:Warning: the following functions are not known to be defined: my-inc-count,
    my-get-count, my-reset-count


今まで諦めていましたが、 lexical-letの前に以下のような同名の空関数を定義する事で回避できることに気がつきました。

(defun my-inc-count () ())
(defun my-reset-count () ())
(defun my-get-count () ())


ちなみに lexical-let の中で関数を定義している部分のマクロの展開結果は以下のようになっています。

(let ((--cl-count-- (make-symbol "--count--")))
  (set --cl-count-- 0)
  (progn
    (progn
      (defalias 'my-inc-count
        (list 'lambda
              '(&rest --cl-rest--)
              (list 'apply
                    (function
                     (lambda (G30002)
                       (set G30002 (1+ (symbol-value G30002)))))
                    (list 'quote --cl-count--)
                    '--cl-rest--)))
      'my-inc-count)
    (progn
      (defalias 'my-reset-count
        (list 'lambda
              '(&rest --cl-rest--)
              (list 'apply
                    (function
                     (lambda (G30003) (set G30003 0)))
                    (list 'quote --cl-count--)
                    '--cl-rest--)))
      'my-reset-count)
    (progn
      (defalias 'my-get-count
        (list 'lambda
              '(&rest --cl-rest--)
              (list 'apply
                    (function
                     (lambda (G30004) (symbol-value G30004)))
                    (list 'quote --cl-count--)
                    '--cl-rest--)))
      'my-get-count)))


clパッケージの提供するマクロの展開結果を調べるには、以下のようなマクロを展開するマクロを書くと便利です。

(defmacro pp-mac* (macro)
  (require 'pp)
  `(pp
    (cl-prettyexpand (quote ,macro)) (get-buffer (current-buffer))))