header

2014年12月22日月曜日

Emacsのcl-lib.elとlexcal-binding: tのエラー回避

最近mykie.elというEmacsのキーバインド拡張をcl-lib対応した。その時に遭遇した問題をメモとして残しておく。(この記事は2014/12/20に書いたので、そのうち修正さる挙動がかわるかもしれません)


cl-libって?

まずcl-libが何だという人向けに説明すると、cl-libはEmacsでもcommon-lisp系の関数を使えるようにする拡張です。loopとかloopとかloopとか(それくらいしかしらない)

たしかEmacs24くらいから正式に導入されたと思いますが、それ以前のEmacserは



(eval-when-compile (require 'cl))

としてclライブラリを使っていました。eval-when-compileの説明をみるとどうやら、コンパイル時評価するようにしていたようです。

詳しい経緯は知らないですが、最近は上記のcl-libを使うほうが主流のようです。


lexcal-baidingって

emacs-lispの変数はダイナミックスコープですが、ファイルの先頭にlexical-binding: tとして追加しておくとそのファイルをレキシカルスコープとして扱ってくれます。



;; some thing ......  -*- lexical-binding: t -*-

問題

んで、mykie.el ですが上の古いclを使って書いてました。去年僕が書いた時はcl-libを使おうとするとwarningが出て直し方がよくわからなかったので、古い方のclでいいやって思ってそのままでしたが、最近このmykie.elパッケージをいじるのにcl-libにしてたらまだワーニングがでたのでいいかげん調べてみました。

最初の問題としては以下のようなcl-loop関数でunused lexical variable my_listと出るようになりました。



;;; this is sample -*- lexical-binding: t -*-
(require 'cl-lib)
(cl-loop with my_list
for str in '(1 2 3)
collect str into my_list
finally return my_list)

上の関数をmacroexpand関数で展開してみると以下のようになりました。



(cl--block-wrapper
(catch (quote --cl-block-nil--)
(let* ((my_list nil)
(--cl-var-- (quote (1 2 3)))
(str nil)
(my_list nil))
(while (consp --cl-var--)
(setq str (car --cl-var--))
(setq my_list (nconc my_list (list str)))
(setq --cl-var-- (cdr --cl-var--)))
my_list)))

なぜかletフォームの中にmy_list nilが2個あるのがわかると思います。というわけで、実際マクロ展開すると使われていない変数として存在するのがわかりました。

じゃあどう書けばいいかっていうと、単純にwith my_listで宣言している部分を消してしまえばいいだけでした。

他のunused variableが出た時のtipsとして、_を変数名につけるとwarningがでなくなるかもしれません。



  •   ;;; this is sample -*- lexical-binding: t -*-
    (require 'cl-lib)
    (cl-loop for (first second) in '((1 . 2) (3 . 4))
    collect first)

    ;; こっちはエラーでない
    (cl-loop for (first _second) in '((1 . 2) (3 . 4))
    collect first)

ただemacs-lispコンパイラーに好き嫌いがあるみたいで、上で紹介したwith文の場合は_ではwarning抑制できませんでした。

あとこれは、あまりおすすめできませんが、with-no-warningsという関数でくくればその中はワーニングが抑制できます。ただ上のwith文は(以下略)

なので、最初は_をちょろっと追加してダメなら、他を試すといった感じでしょうか

あとこんなスーパーおっちょこちょいは僕だけかもしれませんが、古いcl-libが読み込まれているとエラーが出るというケースもあるみたいです。いいかたがあやふやなのはもう古いのは消してしまって確認するのがめんどうという理由ですが、確か古いcl-lib消したらちょっとワーニング変わった気がします。たぶん

でどうやって確認するかですが、M-x list-load-path-shadowsで確認できました。
(僕がみつけたのは、なんかwarningにそれっぽいのがでてたからですが)

こんなかんじの出力でした。



/home/yuta/local/vcs/github.com/yuutayamada/emacs.d/elisp/el-get/cl-lib/cl-lib hides /home/yuta/local/vcs/git.savannah.gnu.org/emacs/lisp/emacs-lisp/cl-lib

おまけ

pcaseでも以下のようにするとunusedワーニングがでました。



(let ((name "おれだよおれおれ"))
(pcase name
(`"foo" "fooだよ")
(`"bar" "barだよ")
(`"piyo" "piyoだよ")
(name (concat name "だよ"))))

一番下のデフォルトで結果を返すところをtにすれば普通にうごきます。


おわり

というわけで、僕がはまったところのネタがつきました。cl-libではまったとき参考になれば幸いです。


0 件のコメント:

コメントを投稿

Popular Posts

Blogger templates

Blogger news