摆脱“引用自由变量”字节编译警告

时间:2012-09-14 21:16:23

标签: emacs elisp

我正在编写一个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现在到处都是绑定的,这在我看来是不受欢迎的。

有没有办法摆脱警告,同时仍然没有绑定每个缓冲区中特定于模式的变量?或者当我认为这些变量不应该全局声明时,我错了吗?

2 个答案:

答案 0 :(得分:42)

做你想做的事的官方方式是(defvar foo-state)。注意没有第二个参数。还要注意,这样的声明仅适用于找到它的文件(或者它在找到它的范围内,如果它在函数中使用的话)。

答案 1 :(得分:36)

使用defvar声明变量。没有其他方法可以删除警告,这确实是一种很好的做法。

你保持符号表整洁的意图是值得的,但你实际上并没有这样做。我认为您误解了Emacs Lisp中变量绑定的语义,因为您似乎相信通过不声明foo-state将在未使用foo-mode的任何缓冲区中解除绑定。 情况并非如此

在Emacs中,Lisp名称(又名符号)是 global 。第一次评估foo-state时,运行时为foo-state创建一个新的符号对象,并将其放入全局符号表(又名obarray)。没有本地符号表,因此评估foo-state的位置和foo-state如何在任何地方引用相同的符号对象无关紧要(见Creating Symbols)。

每个符号对象由组件(也称为单元格)组成,其中一个是变量单元格(请参阅Symbol components)。 setq修改系统的当前绑定,在顶层没有词法绑定,这有效地改变了符号对象的变量单元格,从而改变了变量的全局值。同样,评估setq的位置无关紧要。实际上,如果某些bar-mode评估为(setq foo-state "bar")foo-state也会被绑定到foo-mode中的“bar”,反之亦然。

因此(defvar)对{em>

(setq)的行为是有意的。您可以将文档附加到变量,并标记为在缓冲区中定义(foo-mode将提供跳转到定义的链接。)

由于Emacs Lisp缺少命名空间,并且 - 默认情况下 - 动态范围,因此文档对于避免模块之间的冲突至关重要。如果我使用您的C-h v foo-state写了bar-mode,我可能会意外绑定到foo-mode,请致电foo-state,然后看到我的模式行为不当,因为变量无意中被覆盖了。声明foo-change-state不会使这个变得不可能,但它至少可以让我捕获错误,因为foo-state会显示这个变量被另一个模式使用,所以我最好不要使用它,除非我真的打算操纵那种模式。

总而言之:在所有上述文本中,“模式”可以替换为Emacs Lisp文件。关于符号,C-h v foo-state并不特别。以上所有内容也适用于Emacs Lisp,它不会声明模式,只包含一堆函数。