6

The following emacs lisp file is about seeing what happens when Alice uses a lexically bound local variable foo in her init file and Bob defines foo as a global special variable with defvar in his init file and Alice borrows part of Bob's init file code into her own init file not knowing that foo is going to turn special.

;; -*- 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))))

Output:

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

(:R3 (10 20 30) :R4 (1000 2000 3000))

Results R1 and R2 are just as what I expect. Result R4 is consistent with defvar documentation, although it may surprise Alice unless she reads Bob's code.

  1. I find R3 surprising. Why is R3 like that?

  2. Speaking of R4, what can Alice do to protect her foo from turning special by others? For example, foo may be a lexical local variable she uses in her init file or one of her emacs package, and (defvar foo "something") may be in some of the packages she happens to use, or foo could be one of the new special variable names introduced by a future version of Emacs. Is there something Alice can put in her file that says to Emacs "In this file, foo should be always lexical, even if some code from outside happens to use a special variable of the same name"?

4

2 回答 2

4

到底是怎么回事

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

编译

如果您将代码(即 2 defuns 和该;; -*- 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))

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

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

一切都很好!

口译

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

(:R1 (10000 20000 30000) :R2 (10000 20000 30000))

将此报告给 emacs 维护人员

不同的???

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

你应该做什么

没有办法“保护”你foo不被其他人宣布special——除非在“你的”符号前加上alice-.

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

于 2013-07-02T15:24:20.837 回答
1

至于第二个问题,据我所知,没有。但是仍然可以解决。那就是采用两种命名约定:我称之为黄色和绿色。

黄色命名约定

所有特殊变量都必须有黄色名称。黄色名称是包含至少一个连字符的名称。例如hello-worldga-na-da是黄色的名字。官方的 Emacs Lisp 手册和字节编译器鼓励这种约定。

绿色命名约定

绿色名称是不是黄色的名称。例如,helloworldganada是绿色的名字。

所有词法非局部/自由变量必须有绿色名称。什么是非局部变量?匿名函数的主体中alice-multiplier-2提到了三个名称,n, foo, num。在这三个中,只有n在(匿名函数的)函数体内有一个声明。从匿名函数的角度来看,另外两个是非本地的。它们是非局部变量。

只要 Alice 和 Bob 坚持这两个命名约定,一切都很好。即使他们现在没有,很可能两个人最终会在没有相互沟通的情况下自行收敛到这些约定,甚至通过这些阶段:

  1. 爱丽丝和鲍勃不遵守任何约定。

  2. 由于手册和字节编译器鼓励使用黄色命名约定,因此 Alice 和 Bob 都开始坚持使用黄色约定。

  3. Alice 采用绿色约定,至少可以保护她的代码免受其他人的黄色特殊变量的影响。

  4. Alice 和 Bob 都遵守这两个约定。

有关可能发生碰撞的精确条件,请参阅特殊变量的入侵

于 2013-08-14T05:41:34.373 回答