Emacs Lisp が「書ける」ようになるまで

先日の関西Emacsでは、Emacs Lisp の入門記事がうけるとか、「書ける」ようになる情報がないらしいという話を聞きました。確かに、Emacsの初歩的な操作入門と、良く書ける人がアプリやブログなどでelispをばしっと書いてるのはよく見ます。入門から書けるようになるまでの間の過程は見たこと無かったかもしれないと思いました。

ということで、Emacsの先人たちにはまだまだ及びませんが、いくつかのアプリを書いてみた自分がたどった方法を書いてみます。先にまとめると以下の2つになると思います。

  • elispでどうしても書きたい物があること
  • たくさん書いて動かして(こっちが先)、そしてInfoや他人のコードを見て勉強する

書ける以前のまとめ

  • Emacs歴10年以上。すでに生活の一部。
    • でも設定を書くのに使っていたぐらい。
    • 既存のアプリの分かりやすいバグを手元でちょっと直すとか。
  • LISP系の言語は、SICPとかでSchemeはわりと書いていたぐらい。
  • Javaとか他の言語ではアプリはたくさん書いていた。

書こうと思った動機

小さい関数は書いていましたが、それだけではあまり書けている気分はしてませんでした。とにかく、メジャーモードかマイナーモードを書かないと「elisp書いた!」と言えない気がしていました。

丁度その頃、Linux上のSkypeを使い始めたのですが、あまりの使いにくさに驚いてこれは何とかしなければと思っていました。既存のコードでEmacsSkypeを操作出来ないかいろいろ調べたのですが、欲しい物が全然見つかりませんでした。

しばらくSkypeAPIを調べてEmacsdbusで実験しているうちに何となく自分で作れそうな手応えを感じたので、「Emacs上でSkypeクライアントを作る」ということが強い目標になりました。

多分、「無いものは作ってしまう」とか「実験しているうちに作れそうな手応えを感じた」というところは、そうあるものではないかもしれませんが、他の言語でたくさんの開発経験があったのでそう思えたのだと思います。

分からないなりに調べて書いてみる

まず、emacs-dbusの情報は当時(2008年頃)ほとんど無かったので、EmacsのInfo(英語)と手元での実験で情報をまとめていきました。

次にチャットを表示するメジャーモードが必要なんだろうなとぼんやり思っていて、以下のページを読みました。

「優しい〜」の方は、elispを書き始めると普通に欲しい情報がそろっていますのでかなり参考になると思います。が、最初読んだときは分からないことが多すぎてあまり理解できていませんでした。

ありえるさんの方は具体的で内容も短く、取りかかりとして丁度良かったです。また、コードは写経しました。

これでテキスト処理についてはある程度書けるようになりました。

この時点では、コードはすごく慣れてない感じのコードです。Schemeの影響を強く受けていて、特にループはmap系関数を使っていましたが、どうしても冗長なコードになっていました。ループしたいのに深い再帰ができないというのもかなりの足かせです。また、「funcall」を書かないと関数が呼べないことや、関数内関数が書けないことや、デフォルトでレキシカルスコープでなかったり、Schemeに比べてかなり書きづらい印象がありました。

また、フォントや色、画像などを表示したかったり、ウインドウの制御をしたかったのですが、どういった検索キーで探せばいいのか、どんな情報セットが必要なのか、さらにはどう書けばいいのか全然分かりませんでした。

リファレンスを読む

ということで、体系的な情報を得るために Emacs Lisp のリファレンスを読むことにしました。日本語版は以下の21.3版がありますので、これを携帯のブラウザから毎日読んでました。

最新の情報は、Emacs23に付属の英語版Infoで補いました。目次をざっと眺めながら、追加されている項目を読んでいく感じです。

また、他の人のelispコードをいろいろ読んでみると、clパッケージをかなり使っていることが分かりましたので、clパッケージのリファレンスも読みました。以下、参考になるURLです。

これで、各知識が頭の中で整理されて、elispの情報のインデックスが構築されました。

レキシカルスコープやループの書き方の違和感については、lexical-letやloopマクロで解消しました。特にloopマクロは相当便利です。いきなり全部の句を覚えようとせずに、まずはdolistやmapcの代わりから少しずつ使い始めれば良いと思います。

また、funcallや名前空間の問題についても、気持ちの問題だと切り替えることで解決しました。Javaの人が「JavaScriptのクラスではプロパティが基本全部public」とか「Rubyは宣言無しで変数を使える」ということに慣れるようなものだと思います。言語にはそれぞれ文化があるので、以前の言語の文化に固執するのは良くないですね。(一方で、他の言語にあるアイデアを持ってくることでブレークスルーが開けることもあるので、いっぱい書いて読んで、コードについて考えることが必要なのかなと思います。)

ここまで来て、かなりのコードが書けるようになりました。ただ、まだまだ未知の領域がありました。

マクロ

他人のソースを見ていて、唯一読めないところがマクロでした。何となく黒魔術のような気がしていて、近づかない方が良いのかもしれないと思っていました。

そのとき、丁度 OnLisp を読んでいました。実はこれが何について書いてある本だか知らずに読み始めたのですが、相当衝撃を受けたことを覚えています。自分の人生の中でSICPの次に衝撃を受けたかもしれません。この件についてはまた後ほど書きたいことがあるのですが、とりあえず一気に自分にマクロ力がつきました。

読めるコードの範囲が広くなり、(マクロを使わないにしても)書くコードの質が上がったような気がします。

ということで、読めるコードが制限されることは良くないので、elispを「書ける」ようになるにはマクロは身につけていた方が良いのではないかなと思います。

自分のルールでは、極力関数ですむところは関数で書いて、抽象化の道具としてはあまりマクロは使わないようにしていますが、マクロで書いた方が見通しが良くなる(書き手の意図が伝わりやすい)ようなところ(例えば aif, acond など)にはよく使っています。また、マクロでしか出来ないコードもありますので、そういうときには積極的に使っています。

たくさん書いて、たくさん読む

ここまでで一通りやりたいことが実現できるようになり、skype.elが何とか完成しました。もちろんダサイところがたくさんあります。書ける領域が広がると楽しいもので、あれもこれも作れそう、作りたいと思うようになりました。

この時点では、大抵のやりたいことは出来るのですが、もう少し高いレベルの書き方で詰まっていました。カプセル化・隠蔽をどうしたらいいのかとか、公開用の関数と内部用の関数をどう分けたらいいかとか、データはどう保持して全体の設計はどういうふうにしたらいいのかとか、あとはコメントとかドキュメントをどう書くべきかなど、いろいろと試行錯誤の連続でした。

そういう問題意識がある時に他の人のコードを読むと参考になります。emacslispディレクトリとか、EmacsWikiとか宝の山です。上の場合は、結局あんまり統一された方法が無く、みんな自分なりに試行錯誤しているということが分かったりするわけですが、それはそれでそういうものなのかと安心したり、人のコードを参考にしつつ自分なりの方法を模索していきました。

今はCLの本などでCLらしい設計について勉強しようとしています。

自分の今後

とりあえず書きたいコードがたくさんあります。また、読んでおきたいコードもたくさんあります。

あとは、elispの速度自体にはあまり不満はないのですが、今後は高速化の手段のひとつとしてcclを知りたいなと思っています。

まとめ

ということで、自分のelispの勉強の歴史を書いてみました。
elispを書きたい皆さんのお役に立てると幸いです。


おまけとして、StackOverFlowの記事がおもしろかったのでちょっと紹介したいと思います。

  • The single most useful Emacs feature - Stack Overflow
    • Emacsの機能の中で好きなところは何ですか?という問いに対して、いろいろな人がコメントしている。
    • 投票1番が「M-/」の補完、2番目が align-regex、以下たくさん続いている
    • Emacsの良いところをひとつだけあげるというのは難しいですね。