Get rid of “reference to free variable” byte-compilation warnings

与世无争的帅哥 提交于 2019-11-27 13:39:00

问题


I'm writing an emacs major mode, which uses buffer-local variables to store some state:

(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"))

This works very well and has the property that in any buffer not using my major mode, the foo-state variable is not bound (which is a good thing in my opinion, since it avoids cluttering the symbols table).

However, byte-compiling such a piece of code produces the following warning:

Warning: assignment to free variable `foo-state'

Using defvar gets rid of the warning, but has the side-effect that foo-state is now bound everywhere, which is undesirable in my opinion.

Is there a way to get rid of the warnings while still not binding the mode-specific variables in every buffer? Or am I mistaken when I think these variables should not be declared globally?


回答1:


The official way to do what you want is (defvar foo-state). Note the absence of a second argument. Note also that such a declaration only applies to the file where it is found (or to the scope in which it is found, if it's used inside a function).




回答2:


Declare the variable with defvar. There is no other way to remove the warning, and it is really considered good practice.

Your intention to keep the symbol table uncluttered is worthy, but you are not actually doing so. I think you have misunderstood the semantics of variable bindings in Emacs Lisp, since you seem to believe that by not declaring it foo-state will be unbound in any buffer not using foo-mode. That is not the case.

In Emacs Lisp names (aka symbols) are global. As soon as foo-state is evaluated the first time, the runtime creates a new symbol object for foo-state and puts this into the global symbol table (aka obarray). There are no local symbol tables, so it does not matter where foo-state is evaluated and how, foo-state refers to the same symbol object at any place (see Creating Symbols).

Each symbol object consists of components (aka cells), one of which is the variable cell (see Symbol components). setq modifies the current binding of the system, on top-level without lexical binding this effectively changes the variable cell of the symbol object, thus the global value of the variable. Again, it does not matter where setq is evaluated. Actually if some bar-mode evaluated (setq foo-state "bar"), foo-state would be bound to "bar" in foo-mode too, and vice versa.

Thus the only effect of (defvar) over (setq) it that documents the intention of using a symbol as global variable, so telling others to not modify this variable unless manipulation of the behavior of foo-mode is intended. You can attach documentation to the variable, and mark as being defined in your buffer (C-h v foo-state will provide a link to jump to the definition).

Since Emacs Lisp lacks namespaces and is – by default – dynamically scoped, documentation is fundamentally important to avoid conflicts between modules. If I wrote a bar-mode using your foo-mode I might accidentally bind to foo-state, call foo-change-state and then see my mode misbehaving because a variable was unintentionally overwritten. Declaring foo-state doesn't make this impossible, but it at least allows me to catch the error, because C-h v foo-state will reveal that this variable is used by another mode, so I'd better not use it unless I really intend to manipulate that mode.

As a last word: In all of the aforementioned text “mode” can be replaced with Emacs Lisp files. modes are nothing special with regards to symbols. All of the above also holds for Emacs Lisp that do not declare modes, but just contain a bunch of functions.



来源:https://stackoverflow.com/questions/12432093/get-rid-of-reference-to-free-variable-byte-compilation-warnings

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!