`progv` 中 `symbol-value` 的行为

问题描述

考虑到动态和词法绑定变量的行为,我理解下面代码symbol-value输出(动态绑定变量a被词法绑定变量a遮住了(那个解释是错误的,见下面的编辑)):

(defvar a 1)
(let ((a 2))
  (list a (symbol-value 'a)))
 ; => (2 2)

但是当使用 progv 创建类似的环境时,symbol-value 给出了不同的结果:

(progv '(x) '(1)
  (let ((x 2))
    (list x (symbol-value 'x))))
 ; => (2 1)

为什么在第二个示例中 (symbol-value 'x) 返回 1

接受的答案附带的最终编辑: 在 Rainer Joswig 的回答的整个评论中,我了解到 (let ((a 2)) ... ) 不绑定词法变量,而是隐藏了前一个动态绑定的值。 Martin Buchmann 在评论中还指出,symbol-value 会忽略词法变量。

解决方法

PROGV 特殊形式创建动态绑定,但没有将变量声明为特殊封闭形式。

因此我们需要声明变量 LETx 绑定是特殊的:

CL-USER 27 > (progv '(x) '(1)
               (let ((x 2))
                 (declare (special x))
                 (list x (symbol-value 'x))))
(2 2)

DEFVAR OTOH 声明它的变量是特殊的。在全球范围内,没有直接的方法可以撤消该声明。