我认为,准确地突出变量绑定范围甚至是不可能的,或者至少不会涉及到字节编译器(或其部分),或重新实现Emacs Lisp语义的部分。
关键问题在于宏。在Emacs Lisp中,宏不是卫生的。因此,
任何宏都可以引入
任意的本地绑定。事实上,许多宏都这样做,例如标准库中的
dolist
、
condition-case
和
pcase
,以及第三方库
dash.el中的anaphoric列表处理函数等。
对于这些宏,仅从句法上下文中确定变量作用域变得
不可能。看下面的例子:
(condition-case err
(--each my-fancy-list
(my-fancy-function it nil t))
(error (message "Error %S happened: %s" (car err) (cadr err))))
不知道
condition-case
和
--each
的情况下,
err
和
it
是否局部绑定?如果是的话,在哪些子表达式中绑定,例如
err
在所有子表达式中都绑定,还是只绑定处理程序形式(后者是这种情况)?
要确定这种情况下的变量作用域,您需要维护一个详尽的宏白名单以及它们的绑定属性,或者您需要扩展宏以动态确定它们的绑定属性(例如在扩展的主体中查找
let
)。
这两种方法在实现时都需要大量的工作,并且存在缺点。宏定义的白名单几乎自然而然地是不完整、不正确和过时的(只需看一下
pcase
的复杂绑定语义),而扩展宏需要宏定义存在,这并不总是事实,例如如果您正在使用上述dash.el编辑Emacs Lisp,而实际上没有安装此库。
扩展宏可能是最佳的解决方案,而且更好的是,您不需要自己实现。Emacs Lisp字节编译器已经完成了这项工作,并警告有关自由变量引用以及启用词法绑定后未使用的词法变量。因此,请
对文件进行字节编译!
如果可以的话,最好避免在运行中的Emacs中调用
byte-compile-file
,而是编写一个
Makefile
在新的Emacs实例中进行字节编译,以获得一个干净的环境:
SRCS = foo.el
OBJECTS = $(SRCS:.el=.elc)
.PHONY: compile
compile : $(OBJECTS)
%.elc : %.el
$(EMACS) -Q --batch -f batch-byte-compile $<
在更复杂的库中,使用
-L
标志来设置适当的
load-path
以进行编译。