该警告提供的价值不大。该变量由先前的顶级赋值绑定,该赋值在全局环境中创建一个绑定,使该变量成为全局变量。它只是被访问,这可能是程序员的意图。
当没有看到变量的定义时,未绑定变量警告非常有价值,因为它会捕获变量引用的拼写错误。但是一个顶层setf
还是setq
应该被实现所注意并被当作一个定义来对待。
当变量由顶级定义setf
,然后受制于以下情况时,发出警告很有用let
:
(setf n 42)
(let ((n 43)) (func))
在这里,看起来程序员可能期望n
成为一个特殊变量,但它不是这样定义的。func
将看到拥有 42 的顶级绑定n
,而不是n
拥有 43 的词汇。所以这里有充分的理由进行诊断。代码的意图需要n
通过defvar
or声明defparameter
,或声明为特殊。
(当然,我们不能警告什么(let ((n 43)) ...)
时候没有看到顶层n
,因为这是绝大多数词法变量的常见情况。)
ANSI Common Lisp 有一点缺陷,3.1.2.1.1 Symbols as Forms一节说只有三种变量:动态、词法和常量。动态变量是声明为特殊的变量,因此根据这种推理,setf
不足以创建动态变量。但是,第3.1.1.1节清楚地说明存在一个全球环境。令人困惑的是,它说它包含具有无限范围和范围的绑定,并且它包含动态变量。但是“动态变量”在术语表中被定义为在动态环境中具有绑定的变量,而动态环境被定义为包含具有“动态范围”的绑定。所以这不可能是全球环境。你知道,3.1.1.1 说的那个包含动态变量!
无论如何,所有这些 ANSI 混淆都造成了一种误解,即认为setf
或setq
不能创建变量,这为伪造的编译器诊断提供了支持。