词法绑定仅在现代版 Emacs Lisp 方言中可用。(See 选择 Lisp 方言.) 使用词法绑定的变量具有 词法作用域(lexical scope),意思是:对该变量的任何引用,在文本上都必须位于该绑定结构内部。下面是一个示例:
(let ((x 1)) ;xis lexically bound. (+ x 3)) ⇒ 4 (defun getx () x) ;xis used free in this function. (let ((x 1)) ;xis lexically bound. (getx)) error→ Symbol's value as variable is void: x
在此示例中,变量 x 没有全局值。当它在 let 表达式内被词法绑定时,仅能在该 let 表达式的文本范围内使用。但从 let 表达式中调用 getx 函数时,却 无法 在该函数内部使用这个变量 — 因为 getx 的函数定义出现在 let 表达式本身的外部。
词法绑定的工作机制如下:每个绑定结构都会定义一个 词法环境(lexical environment),该环境会指定在这个结构内被绑定的变量及其局部值。当 Lisp 求值器需要获取变量的当前值时,会首先在词法环境中查找;如果变量未在词法环境中定义,则会去符号的值单元(value cell)中查找(动态值即存储于此)。
词法绑定具有无限存在期。即便绑定结构执行完毕,其词法环境仍可通过名为 闭包(closures) 的 Lisp 对象 “留存(kept around)” 下来。当你在启用词法绑定的情况下定义命名函数或匿名函数时,就会创建闭包。See 闭包。
当闭包作为函数被调用时,其定义内的所有词法变量引用都会使用留存的词法环境。以下是一个示例:
(defvar my-ticker nil) ; We will use this dynamically bound ; variable to store a closure. (let ((x 0)) ;xis lexically bound. (setq my-ticker (lambda () (setq x (1+ x))))) ⇒ #f(lambda () [(x 0)] (setq x (1+ x))) (funcall my-ticker) ⇒ 1 (funcall my-ticker) ⇒ 2 (funcall my-ticker) ⇒ 3 x ; Note thatxhas no global value. error→ Symbol's value as variable is void: x
在这里,let 绑定定义了一个词法环境,其中变量 x 被局部绑定为 0。在这个绑定结构内部,我们定义了一个 lambda 表达式,它将 x 加 1 并返回递增后的值。这个 lambda 表达式会自动变成闭包,其中的词法环境即使在 let 绑定结构退出后依然会继续存在。每次我们对这个闭包求值时,它都会使用那个词法环境里的 x 绑定,并对 x 进行加 1。
注意:与动态变量直接绑定到符号对象本身不同,词法变量与符号之间的关系只存在于解释器(或编译器)内部。因此,接收符号作为参数的函数(如 symbol-value、boundp 和 set)只能获取或修改变量的动态绑定(即符号的值单元里的内容)。
还要注意:变量可以被声明为 special,此时即使是新的绑定(例如 let 绑定)也会使用动态绑定。根据变量声明方式的不同,它可以是全局特殊、单个文件内特殊,或文件中某一段代码内特殊。See 动态绑定。