不是完全透明的,我以前找到过反例
不过我当时对 lisp 理解还不够,这样写就可以了
(let ((p '(x . 1)))
(setq foo
`(closure (,p t) (y)
(setq x y)))
(setq bar
`(closure (,p t) ()
x)))
(funcall foo 2)
;; 2
bar
;; (closure ((x . 2) t) nil x)
(funcall bar)
;; 2
Emacs Lisp 的 printer 本身对 sharing 就不是很透明。
(setq print-circle t)
foo
;; (closure ((x . 2) t) (y) (setq x y))
(list foo bar)
;; ((closure (#1=(x . 2) t) (y) (setq x y)) (closure (#1# t) nil x))
SBCL 的设计是只有 compiler,没有 interpreter,所有对 eval 的调用是 just in time compile。这样就没有不一致的问題。然后因为和 CLOS MOP 的 funcallable instance 设计冲突没有透明的 closure 实现也很正常。
有些比较简易的 Scheme 的 closure 实现是透明的。我记很 festival TTS 用的 Scheme 就是这样 https://www.cstr.ed.ac.uk/projects/festival/manual/
Emacs Lisp 的 byte-compile
需要利用 lexical scoping 的 renaming 去除局部变量来相对 dynamic scoping 提高运行效率。