在Emacs Lisp中给自由变量赋值有什么危险?

4

如果在不使用defvar定义变量foo的情况下,将(setq foo 1)添加到代码中,将会导致Emacs字节编译器生成以下警告:

assignment to free variable `foo'

没有对自由变量进行先前定义而分配给它的危险是什么?
1个回答

9

Emacs Lisp默认使用动态绑定,因此当您编写以下代码时:

(defun my-fun-1 (...)
  ...
  (setq foo 1)
  ...)

当你收到警告时,这段代码相当于在defun之前写了(defvar foo)

这意味着上面的my-fun-1foo共享值。

(defun maybe-start-ww3 ()
   (when (= 1 foo)
     (launch-missiles)))

这并不总是不好的,许多ELisp旧代码使用此类全局变量来传递状态。但是,如果您在两个不相关的地方使用相同的变量名(例如date)来做这些事情,则后果可能是不可预测的,因此这是一种非常糟糕的风格。

简而言之,如果您在单个位置使用自由变量,则没有理由不将其绑定。如果您使用它来传递状态,则应该defvar它,并使用一个长名称,不太可能被他人使用,并且您应该重构您的代码以避免使用该变量。


所以,如果我理解你的意思正确的话,如果目标是设置(或定义和设置)一个全局变量,例如在包内、具有长名称的包专用变量,则不使用 defvar 基本上是无害的。 - Andrzej Pronobis
3
确实,无害但是愚蠢的。记住,你编写代码是为了让别人(以及明年的自己!)_阅读_它,而不仅仅是让计算机执行它。添加 defvar 将会增加一份重要的文档说明。 - sds
完全同意。然而,我的情况有些不同。目标是在宏内部生成具有动态生成名称的全局变量。defvar 不会评估给定的符号,这使得在此上下文中使用它不可能。 - Andrzej Pronobis
1
即使在你的情况下,也有一种避免警告的方法(在CL中,你可以使用gensymdeclare等)。请提出一个单独的问题。 - sds
1
@And: 确实,defvar 不会对其给定的符号进行求值,但你也不需要这样做,因为你的宏可以返回形式为 (progn (defvar <foo>) ...) 的内容,这样你就可以构建 defvar,在宏展开的代码运行时执行一次。 - Stefan
警告只是一个警告而已;但是污染全局命名空间是一个坏习惯,而且通常你会因为拼错了正确定义的变量而收到警告,在开发过程中这当然也是有用的。 - tripleee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接