emacs lisp中词法绑定和defvar之间奇怪的交互

Jis*_*Yoo 6 lisp emacs elisp dynamic-scope lexical-scope

以下emacs lisp文件是关于当Alice foo在她的init文件中使用词法绑定的局部变量并且Bob 在他的init文件中定义foo为全局特殊变量defvar并且Alice将Bob的init文件代码的一部分借入她自己的init文件时发生的事情.知道这foo会变得特别.

;; -*- lexical-binding: t; -*-
;; Alice init file

;; Alice defining alice-multiplier
(defun alice-multiplier-1 (foo)
  (lambda (n) (* n foo)))
(defun alice-multiplier-2 (num)
  (let ((foo num))
    (lambda (n) (* n foo))))

;; Alice using alice-multiplier
(print
 (list
  :R1 (mapcar (alice-multiplier-1 10) (list 1 2 3))
  :R2 (mapcar (alice-multiplier-2 10) (list 1 2 3))))

;; from Bob's code
;; ...    
(defvar foo 1000)
;; ...

;; Alice using alice-multiplier
(print
 (list
  :R3 (mapcar (alice-multiplier-1 10) (list 1 2 3))
  :R4 (mapcar (alice-multiplier-2 10) (list 1 2 3))))
Run Code Online (Sandbox Code Playgroud)

输出:

(:R1 (10 20 30) :R2 (10 20 30))

(:R3 (10 20 30) :R4 (1000 2000 3000))
Run Code Online (Sandbox Code Playgroud)

结果R1和R2正如我所期望的那样.结果R4与defvar文档一致,但除非她读取Bob的代码,否则它可能会让Alice感到惊讶.

  1. 我发现R3令人惊讶.R3为什么这样?

  2. 说到R4,爱丽丝可以做些什么来保护她foo免受其他人的伤害?例如,foo可能是她在她的init文件或其中一个emacs包中使用的词法局部变量,并且(defvar foo "something")可能在她碰巧使用的某些包中,或者foo可能是未来版本引入的新特殊变量名之一Emacs.Alice可以在她的文件中添加一些内容,告诉Emacs"在这个文件中,foo应该总是词汇,即使外部的某些代码碰巧使用了同名的特殊变量"?

sds*_*sds 4

到底是怎么回事

从“理论”(Scheme/Common Lisp)的角度来看,一旦启用了词法绑定,对于所有实际目的来说alice-multiplier-1都是相同alice-multiplier-2的。它们行为的任何差异都是 Emacs Lisp 中的错误,应该如此报告。

已编译

如果您将代码(即 2defun;; -*- lexical-binding: t; -*-行)放入一个文件中,emacs-list-byte-compile-and-load那么您可以通过评估这 4 种形式来测试我的主张:

(disassemble 'alice-multiplier-1)
(disassemble 'alice-multiplier-2)
(disassemble (alice-multiplier-1 10))
(disassemble (alice-multiplier-2 10))
Run Code Online (Sandbox Code Playgroud)

您将看到 3 和 4 相同,而 1 和 2 有一条指令不同(这应该作为错误报告给 Emacs 维护者,但不会影响行为)。

请注意,所有反汇编都没有提到foo,这意味着defvar不会影响它们的行为。

一切都很好!

解释

事实上,您看到的行为是不正确的;之后的 正确结果defvar

(:R1 (10000 20000 30000) :R2 (10000 20000 30000))
Run Code Online (Sandbox Code Playgroud)

将此报告给 emacs 维护者

不同的???

是的,defvar 确实(并且应该!)影响解释代码的行为,并且不会(并且不应该!)影响编译代码的行为。

你应该做什么

没有办法“保护”您的符号foo不被其他人声明special- 除非在“您的”符号前面加上alice-.

但是,如果您使用定义对文件进行字节编译alice-multiplier-1,则编译后的文件甚至不包含foo,因此将来的声明foo不会影响您。