在包中覆盖“defun”
Posted
技术标签:
【中文标题】在包中覆盖“defun”【英文标题】:Overriding "defun" within a package 【发布时间】:2019-10-18 09:01:48 【问题描述】:我想从我正在创建的包中定义一个名为“defun”的宏,并且我想将其导出以在某些地方使用。有一个名为 parenscript 的库可以在它的包中执行此操作,
(export #:defun)
当我尝试在自己的包中执行此操作时,我收到此 SBCL 错误
Lock on package COMMON-LISP violated when defining DEFUN as a macro while in package COMMON-LISP-USER.
这在 parenscript 库中是如何完成的?我知道您可以输入表单;
(ps (defun function-name (args) (body)))
我希望能够做到这一点,但不知道这是如何做到的?
【问题讨论】:
他们可能会使用ps
作为宏来定义自己的绑定,从而绕过 SBCL 的限制。
【参考方案1】:
您需要阅读更多关于packages and symbols 的信息。在这里,我将在需要时对所有符号进行限定,以免我在谈论哪个符号时产生歧义。
您不能重新定义 CL:DEFUN
,这会调用未定义的行为,并且您可能会通过使其无法使用来“破坏”您的运行时。这就是为什么 SBCL 对包有 locks 的概念,这是一种避免错误修改包及其绑定的方法(您仍然可以解锁包,但通常不需要这样做)。
在您的宏范围内,您可以随意解释 CL:DEFUN
,这就是 Parenscript 通过将实际 Lisp 代码的子集转换为 javascript 所做的。
在任何其他包P
中,您可以将P:DEFUN
定义为与CL:DEFUN
完全不同的变量/函数/宏/任何内容。你可以导出它,一切都很好,你可以随意使用P:DEFUN
和CL:DEFUN
。
如果您想编写不合格的DEFUN
符号并让读者找出引用了哪些符号,则可能会发生冲突。通常,库的用户可能会这样定义一个包:
(defpackage :foo (:use :cl :p))
这会导致冲突,因为“CL”和“P”包都导出“DEFUN”。解决这个问题的一种方法是定义一个重新绑定DEFUN
并从“CL”重新导出所有其他符号的 Common Lisp 方言。然后,您的用户必须只使用您的包而不是 CL 包。另一种方法是使用 CL 和 shadow-import 仅来自 P 的“DEFUN”,因此 DEFUN
是 P:DEFUN
的别名(因此,您 需要写CL:DEFUN
来显式引用Common Lisp宏)。
上面给出的链接更详细。
【讨论】:
感谢您的回复。那么可以肯定地说 parenscript 在 CL 代码上执行某种形式的代码遍历来执行它的编译吗?这肯定是唯一的方法 @JamesGunn 是的:见gitlab.common-lisp.net/parenscript/parenscript/blob/master/src/…【参考方案2】:您希望隐藏 CL 包中的原始符号。
CL-USER 1 > (defpackage "MY-PACKAGE" (:use "CL"))
#<The MY-PACKAGE package, 0/16 internal, 0/16 external>
CL-USER 2 > (in-package "MY-PACKAGE")
#<The MY-PACKAGE package, 0/16 internal, 0/16 external>
MY-PACKAGE 3 > (shadow 'defun)
T
MY-PACKAGE 4 > (cl:defun defun () :my-defun-returns)
DEFUN
MY-PACKAGE 5 > (defun)
:MY-DEFUN-RETURNS
MY-PACKAGE 6 > (export 'defun)
T
【讨论】:
以上是关于在包中覆盖“defun”的主要内容,如果未能解决你的问题,请参考以下文章