在过去处理Emacs Lisp动态范围陷阱

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)

lun*_*orn 5

Alice会注意不在lambda主体中使用非局部变量,意识到lambda没有创建词法闭包.

在Emacs Lisp中,这个简单的策略实际上足以避免动态范围的大多数问题,因为在没有并发性的情况下,let动态变量的局部绑定大多等同于词法绑定.

换句话说,Emacs Lisp开发人员的"旧时代"(由于动态范围的Emacs Lisp的数量仍然存在而不是那么老)不会写出lambda类似的东西.他们甚至都不愿意,因为Emacs Lisp不是一种函数式语言(并且仍然不是),因此循环和显式迭代通常比高阶函数更受欢迎.

关于你的具体例子,"旧时代"的爱丽丝只会编写两个嵌套循环.


Ste*_*fan 5

Emacs 处理这个问题的方式是遵循一个非常严格的约定:编写高阶函数(例如您fp-repeat的其他人,当光线不起作用时,他们应该做他们的日常祈祷(在 Emacs 教会中总是一个好主意)。