semantic/wisent中存在可能导致闭包捕捉失效的变量


#1

局部变量不被闭包捕捉的凶手找到了 @CEDET

以前Emacs没词法作用域,这样用把用来let-bound使用的变量defvar声明防止byte-compiler报警方式还问题不大。但Emacs24引入词法作用域后,这样声明变量会把变量标记为动态绑定(向后兼容)。如果你恰好用了和他们同名的本地变量,那么你的本地变量将会从闭包中逃逸

试比较

;;; -*- lexical-binding: t; -*-

(defvar c)

(funcall (let ((c 3))
           (lambda () (+ c 2))))
;;; -*- lexical-binding: t; -*-

(defvar c)

(funcall (let ((e 3))
           (lambda () (+ e 2))))

后者可以正常加载执行,而前者加载过程中会报错

Load error for /home/chino/tmp/test-leix.el:
(void-variable c)

具体到这个例子,规避的方法如果引用了wisent就不要起这三个名字的局部变量。

另外令我不解的是wisent.el末尾写了

(provide 'semantic/wisent/wisent)

然而我照抄这个symbol却不能require,最后还是用的(require 'semantic/wisent)


#2
⋊> ag '\(provide \'semantic/wisent' ~/emacs-2019-06-26-f0151e17d296bfdeb1ca3f002c9b430c8302a6e7/ -G\.el\$

...

~/emacs-2019-06-26-f0151e17d296bfdeb1ca3f002c9b430c8302a6e7/lisp/cedet/semantic/wisent/wisent.el
476:(provide 'semantic/wisent/wisent)

~/emacs-2019-06-26-f0151e17d296bfdeb1ca3f002c9b430c8302a6e7/lisp/cedet/semantic/wisent.el
342:(provide 'semantic/wisent)

#3

ISLISP 和 EuLisp 标准决定取用动态绑定需要显式用 (dynamic var),估计有这方面的考量。

Common Lisp 可以写

(defun foo (*standard-input*)
 ...)

;; where *standard-input* is special
;; 相当于

(defun foo (stream)
  (let ((*standard-input* stream))
...

#4

Elisp也可以的。代价是byte-compiler报警

Lexical argument shadows the dynamic variable c

#5

有点物理/科幻的味道 :thinking: