autoloadと4種類のロード


EmacsのLispパッケージのロード方法について、あまり周知されてないので書きます。まちがったことを書いてたらごめんね。日本語での説明は@ayatakesiさんの【途中】emacs 24.5のelisp日本語マニュアルより引用します1

TL;DR

  • package.elとかEl-Getを使ってるなら、init.el から、ほとんどの (require 'hoge) は消しちゃっても動くと思うよ
  • あなたがLispパッケージ作者なら、依存関係はオートロードを期待せずに(require 'hoge)は列挙しないとだめだよ

0. load-path

load-pathは、EmacsがLispファイルを読み込むディレクトリ一覧の変数です。もしpackage.elやEl-Getのようなパッケージ管理ツールだけを利用する場合は、この変数を操作する必要はありません

管理ツールを利用せず手動でLispパッケージをダウンロードして個別のディレクトリをロード対象に追加したければ、(add-to-list 'load-path "/path/to/php-mode")のように書きます。もしダウンロードしたLispパッケージを一箇所のディレクトリにまとめてあるのであれば、以下のように記述すると、まとめてload-pathに追加できます。

以下は、~/.emacs.d/lispディレクトリに複数のパッケージをダウンロードして配置する例です。

init.el
(let ((default-directory (locate-user-emacs-file "./lisp")))
  (add-to-list 'load-path default-directory)
  (normal-top-level-add-subdirs-to-load-path))

蛇足ですが、normal-top-level-add-subdirs-to-load-path(let ((default-directory ...)) ...)で括ることで挙動が変化します。これはEmacs Lispの変数がダイナミックスコープ(動的束縛)である如実な特徴です。

1. (require 'feature)

これはもっともよく目にしますが、init.elに書く必要はほとんどありません。後述しますが、(package-initialize)などを実行する前に必要なファイルは必要です。

これは、Lispファイルをフィーチャ(feature, 機能)の単位で管理して、「一回だけ」読み込むようにするものです。

Function: require feature &optional filename noerror

この関数はカレントEmacsセッションにおいて、featureが存在するかどうかを、((featurep feature)を使用して。以下を参照)をチェックします。引数featureはシンボルでなければなりません。

そのフィーチャが存在しない場合、requireloadによりfilenameをロードします。filenameが与えられなかった場合は、シンボルfeatureの名前がロードするファイル名のベースとして使用されます。しかしこの場合、requirefeatureを探すためにサフィックス‘.el’および‘.elc’の追加を強制します(圧縮ファイルのサフィックスに拡張されるかもしれません)。名前がただのfeatureというファイルは使用されません。(変数load-suffixesは要求されるLispサフィックスを正確に指定します。)

noerrorが非nilの場合は、ファイルの実際のロードにおけるエラーを抑止します。この場合、そのファイルのロードが失敗すると、requirenilをリターンします。通常では、requirefeatureをリターンします。

ファイルのロードは成功したがfeatureをプロバイドしていない場合、requireは‘Required feature feature was not provided’のようにエラーをシグナルします。

15.7 Features (2018年3月26日閲覧) より引用。

(require 'hoge)した場合、load-pathに含まれるディレクトリのどこかhoge.elc, hoge.el(動的モジュールが有効な環境では hoge.soなど)があれば、それを読み込み、なかったらエラーが発生します。

「機能があったら読み込んで、なくてもエラーにしない」場合は、(require 'hoge nil t)にします。あなたがLispパッケージ作者なら、必須ではないパッケージの読み込みにも利用できます。

一般的なLispパッケージでは、ファイルの冒頭に(require)を記述します。依存パッケージのうち特定の関数でしか利用せず、その関数を起動するまでは必要ないようなフィーチャは、後述するautoloadで書くと良いでしょう。

2. (load "file")

ファイル名のフルパス"/path/to/hoge.el"を渡した場合は直接そのファイルを、ファイル名だけを"hoge"のように指定した場合は、load-pathからhoge.elhoge.elcを探し出します。

低レベルな関数なので、直接利用すべき場面は、あまりありません。一般的なパッケージの読み込みには(load)ではなく(require)を利用します。

3. (autoload 'func "file")

オートロードは関数名(または、キーマップとマクロ)の名前と定義ファイルの名前の対応を登録して、必要になったとき(関数の実行時)までファイルの読み込みを遅延させる機能です。

この機能は、基本的にはパッケージ管理ツールが自動で読み込んでくれる*-autoloads.elの中に記述されます。パッケージ管理ツールを利用する場合はinit.elなどに手作業で定義する必要はありません

オートロードは便利ですが、(自動生成されるものを除いて)関数ひとつひとつを手作業で登録する必要があるので管理が煩雑になります。読み込み時間が体感できるほど巨大なパッケージを除いて、素直に(require)を書くのが良いでしょう。

4. *-autoloads.el

このファイルはパッケージ管理ツールがインストール時に自動生成して、Emacs起動時に自動で読み込むファイルです。

典型的には以下のような定義が含まれます。

例としてphp-mode.elcがロードされと159KBの読み込みが発生しますが、php-mode-autoloads.elは4KB程度で済むのでEmacs初期化完了までの負荷が軽減されるといった仕組みです。

あなたがLispパッケージ作者ならば、関数定義や変数定義の上の行に;;;###autoloadと書いてやるだけで、パッケージ管理ツールが適切に自動生成してくれます。

パッケージ管理ツールを使用しない場合はupdate-file-autoloadsなどを利用することで手動生成することもできます… が、ちょっとめんどくさいのでオススメ度はあまり高くありません。

つまりどうすればいいの

package.elはべんり。

脚注


  1. この翻訳は作業中のため、リンク切れになる可能性があります。