Jis*_*Yoo 4 emacs elisp dynamic-scope
在过去,Emacs不支持词法范围.我想知道当时人们如何处理动态范围的特定陷阱.
假设Alice编写的命令my-insert-stuff依赖于fp-repeat定义的函数fp.el(我们假设它是一个为Bob编写的函数式编程提供大量函数的库),并且假设fp-repeat是多次重复调用函数.
init.elAlice的部分内容:
(require 'fp)
(defun my-insert-stuff ()
(interactive)
;; inserts "1111111111\n2222222222\n3333333333" to current buffer
(dolist (i (list "1" "2" "3"))
(fp-repeat 10
(lambda ()
(insert i)))
(insert "\n")))
Run Code Online (Sandbox Code Playgroud)
fp.elBob的部分内容:
(defun fp-repeat (n func)
"Calls FUNC repeatedly, N times."
(dotimes (i n)
(funcall func)))
Run Code Online (Sandbox Code Playgroud)
爱丽丝很快发现她的命令不像她预期的那样有效.那是因为Alice的使用i和Bob的使用i相撞.在过去,爱丽丝或/和鲍勃可以做些什么来阻止这种碰撞的发生?
也许Bob可以将文档字符串更改为
"Calls FUNC repeatedly, N times.
Warning: Never use i, n, func in FUNC body as nonlocal variables."
Run Code Online (Sandbox Code Playgroud)
Alice会注意不在lambda主体中使用非局部变量,意识到lambda没有创建词法闭包.
在Emacs Lisp中,这个简单的策略实际上足以避免动态范围的大多数问题,因为在没有并发性的情况下,let动态变量的局部绑定大多等同于词法绑定.
换句话说,Emacs Lisp开发人员的"旧时代"(由于动态范围的Emacs Lisp的数量仍然存在而不是那么老)不会写出lambda类似的东西.他们甚至都不愿意,因为Emacs Lisp不是一种函数式语言(并且仍然不是),因此循环和显式迭代通常比高阶函数更受欢迎.
关于你的具体例子,"旧时代"的爱丽丝只会编写两个嵌套循环.
Emacs 处理这个问题的方式是遵循一个非常严格的约定:编写高阶函数(例如您fp-repeat的其他人,当光线不起作用时,他们应该做他们的日常祈祷(在 Emacs 教会中总是一个好主意)。