为缓冲区启用词法绑定的潜在陷阱是什么?


12

这是通过讨论启发词汇结合 VS 词汇,让此问题。由于词法绑定使您能够使用有用的闭包,人们可能已经习惯于其他语言(如JavaScript),为什么不一直启用它呢?

假定与旧版Emacsen的向后兼容性无关紧要,如果在旧版代码缓冲区中启用它,您应该寻找什么陷阱?

Answers:


13

一个主要的陷阱是未定义变量(即未用defvar定义的变量)的绑定语义随着改变lexical-binding:没有它,let将动态绑定所有内容,但lexical-binding启用后的未定义变量将按词法进行绑定,如果在当前词法范围内未使用,甚至会完全消失。 。

旧代码有时依赖于此。为了避免对可选功能的严格依赖,它会绑定动态变量,无需相应的库或声明变量本身:

(let ((cook-eggs-enabled t))
  (cook-my-meal))

如果烹饪功能是可选的,则我们不想将不必要的依赖关系强加给用户,因此我们不使用功能(require 'cook),而是依靠cook-my-meal功能的自动加载。

对于人类读者来说,显而易见的是,cook-eggs-enabled它不是局部变量,而是在cook此处引用了库中的某些全局动态变量。没有lexical-binding此代码,将按预期工作: cook-eggs-enabled动态绑定,无论是否定义。

随着lexical-binding然而,它打破了: cook-eggs-enabled现在势必词汇(然后优化掉,因为它没有使用),因此全球动态变量cook-eggs-enabled不是曾经在所有的感动,还是 nil由时间cook-my-meal被调用,所以我们出人意料地没有任何鸡蛋在我们的饭菜。

幸运的是,这些问题很容易发现:字节编译器自然会在此处警告未使用的词法绑定。

修复很简单:要么添加一个(require 'cook)(对于实际上并不是真正可选的功能),或者为避免硬性依赖,在您自己的代码中将变量声明为动态变量。defvar为此有一种特殊形式:

(defvar cook-eggs-enabled)

这定义cook-eggs-enabled为动态变量,但不影响文档字符串,文档字符串load-history(以及由此find-variable和朋友)或其他任何变量,除了变量的绑定性质。


在动态情况下,那不是代码片段的原因cook-eggs-enabled未绑定的时候let结束?我敢肯定我以前遇到过这样的错误。defvar发生在内letlet后来该变量将变量恢复为其初始(无效)状态。
马拉巴巴2014年

1
@Malababa不,那是另外一种情况。有关原因,请参见定义变量的最后一段。
lunaryorn 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.