web-dev-qa-db-ja.com

「自由変数への参照」のバイトコンパイル警告を取り除く

私はemacsメジャーモードを書いています。これは、バッファーローカル変数を使用していくつかの状態を格納します。

(defun foo-mode ()
  "My Nice major mode"
  (interactive)
  (kill-all-local-variables)
  (setq mode-name "foo")
  (setq major-mode 'foo-mode)
  (set (make-local-variable 'foo-state) "bar"))

(defun foo-change-state ()
  (setq foo-state "baz"))

これは非常にうまく機能し、私のメジャーモードを使用していないバッファーではfoo-state変数はバインドされていません(シンボルテーブルが乱雑にならないようにするため、これは私の意見では良いことです)。

ただし、このようなコードをバイトコンパイルすると、次の警告が表示されます。

Warning: assignment to free variable `foo-state'

defvarを使用すると警告は表示されなくなりますが、foo-stateはどこにでもバインドされていますが、これは私の意見では望ましくありません。

すべてのバッファーでモード固有の変数をバインドせずに警告を取り除く方法はありますか?または、これらの変数をグローバルに宣言してはいけないと思うとき、私は間違っていますか?

39

あなたがやりたいことをする公式な方法は(defvar foo-state)。 2番目の引数がないことに注意してください。また、このような宣言は、それが見つかったファイル(または関数内で使用されている場合は、見つかったスコープ)にのみ適用されることに注意してください。

43
Stefan

変数をdefvarで宣言します。警告を削除する方法は他にありません。これは本当に良い習慣と考えられています。

シンボルテーブルを整理するという意図は価値がありますが、実際にはそうしていません。 Emacs LISPでの変数バインディングのセマンティクスを誤解していると思います。宣言しないことで、foo-statefoo-modeを使用しないバッファーでバインド解除されると信じているようです。 そうではありません

Emacsでは、LISP名(別名シンボル)はglobalです。 foo-stateが初めて評価されるとすぐに、ランタイムはfoo-stateの新しいシンボルオブジェクトを作成し、これをグローバルシンボルテーブル(別名obarray)に入れます。ローカルシンボルテーブルがないため、foo-stateがどこで評価され、foo-statesameをどのように参照するかは問題ではありません。 任意の場所のシンボルオブジェクトシンボルの作成 を参照)。

各シンボルオブジェクトはコンポーネント(別名セル)で構成され、その1つはvariableセルです( シンボルコンポーネント を参照)。 setqは、システムの現在のバインディングを変更します。最上位レベルでは字句バインディングは行われません。これにより、シンボルオブジェクトの変数セルが実質的に変更されます。つまり、global変数の値。繰り返しますが、setqがどこで評価されるかは問題ではありません。実際、一部のbar-mode(setq foo-state "bar")を評価した場合、foo-statefoo-modeの「bar」にバインドされ、逆も同様です。

したがって、(defvar)に対する(setq)の唯一の効果は、シンボルをグローバル変数として使用する意図documentingであるため、他の人に伝えるfoo-modeの動作を意図したものでない限り、この変数を変更しないでください。変数にドキュメントを添付し、バッファで定義されているものとしてマークできます(C-h v foo-stateは、定義にジャンプするためのリンクを提供します)。

Emacs LISPには名前空間がなく、デフォルトで動的スコープが設定されているため、モジュール間の競合を回避するために、ドキュメントは基本的に重要です。 bar-modeを使用してfoo-modeを作成した場合、誤ってfoo-stateにバインドする可能性があります。foo-change-stateを呼び出すと、変数が誤って上書きされたため、モードが正しく動作しません。 foo-stateを宣言しても、これは不可能になりませんが、少なくともC-h v foo-stateによってこの変数が別のモードで使用されていることがわかるため、エラーをキャッチできます。私は本当にそのモードを操作するつもりです。

最後の言葉として:前述のすべてのテキストで、「モード」はEmacs LISPファイルに置き換えることができます。 modesは、シンボルに関して特別なものではありません。上記のすべては、モードを宣言せず、一連の関数を含むだけのEmacs LISPにも当てはまります。

37
lunaryorn