切换包时词法绑定丢失
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::X
或 BAR::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))
我想要的只是为 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::X
或 BAR::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))