切换包时词法绑定丢失

lexical binding lost when switching package

我想要的只是为 swank 加载一个初始化文件,当它在没有 swank 的情况下启动时不会影响我的 lisp...

我首先在从 ccl-init 加载的文件中尝试 #+swank (defun...)(在 ccl 1.10 + windows 上尝试),很快意识到它是在加载 swank 之前获取的(显然)。

我的目标是在每次启动 swank 时在 :cl-user 中定义一个简单的函数。我刚刚结束了一个 swank add-hook 来加载我的 init.lisp 文件,因为我想在 cl-user 中定义函数,所以我在 init.lisp:

中尝试了这个
(let ((current-package *package*))
  (in-package :cl-user)
  (defun cd (dir)
    (swank:set-default-directory
      (parse-namestring dir)))
  (in-package current-package))

现在,我不记得是否允许在 let 中使用 defun,但 lisp 并没有为此抱怨,而是告诉我 cur-pck 符号不存在,似乎当我们切换包,cur-pck 绑定超出范围。我认为 cur-pck 是一个词法绑定,它应该可以从词法区域内访问,独立于包,我错了吗?

为什么要切换套餐?我在想,在某个初始化点从 swank 加载这个文件会在一些 swank 包中定义一些东西,这就是为什么我想先尝试切换到 cl-user,定义函数符号,然后切换回来让 swank 做它的事情。

在这一点上,我想我需要有人告诉我我从错误的角度处理问题,我最好选择一个更简单的解决方案。

此外,出于好奇,上面的方法是完全错误的方法,有没有办法在函数或闭包中的另一个包中定义符号?

IN-PACKAGE 是宏,不是函数。您的代码中的问题是说 (in-package cur-pck) 试图不切换到 cur-pck 变量表示的包,而是切换到名为 CUR-PCK 的包(显然不存在)。

您可以通过

临时设置套餐
(let ((*package* (find-package :cl-user)))
  (defun cd (dir)
    ...))

不过话又说回来,实现你正在做的事情的最简单方法是

(defun cl-user::cd (dir)
  ...)

这完全消除了设置当前包的需要。

如果您想在与当前包不同的包中定义一个函数,您可以使用限定符号作为名称

(defun cl-user::cd (dir)
    (swank:set-default-directory
      (parse-namestring dir)))

绑定未 "lost"。要自行测试,请在包装内表格前添加 (princ cur-pck)

如果您尝试评估 (in-package *package*),您就会明白为什么您的代码无法切换包。包内宏不评估其参数。为我们提供我们想要评估的代码的代码是:

(let ((cur-pck *package*))
  (in-package :cl-user)
  (defun cd (dir)
    (swank:set-default-directory
     (1+ 2)))
  (princ cur-pck)
  `(in-package ,cur-pck)) 

但是,正如 Rainer Joswig 在他的回答中指出的那样,内包对已阅读的表单没有影响,因此即使作为宏也无法按预期工作。

风格挑剔,不要使用缩写,写当前包。

在表单中切换包对表单没有直接影响

让我们看看这个:

(in-package "FOO")

(let ((x 10))
  (in-package "BAR")
  (setf x 20))

哪个x设置为20? FOO::XBAR::X?

嗯,是FOO::X。在执行期间切换包对已读取的符号没有影响。一次读取整个 let 表单,并使用 *package* 值。在表单本身中包含 IN-PACKAGE 对表单本身没有影响。

带包前缀的符号

如果要使用某个包中的符号,只需写包前缀:

cl-user:foo   ; if FOO is exported and the package exists

cl-user::foo  ; if foo is not exported and the package exists

例如:

(defun cl-user::cd (...) ...)

用符号计算

您还可以计算您还不知道的包中的新符号:

(let ((sym-name "A-NEW-SYMBOL")
      (my-package-name "SOME-EXISTING-PACKAGE"))
  (intern sym-name my-package-name))

如果包不存在,您可以创建它。

您还可以设置计算符号的功能:

(setf (symbol-function (compute-a-function-symbol))
  #'(lambda ()
      'foo))