在包中覆盖“defun”

Posted

技术标签:

【中文标题】在包中覆盖“defun”【英文标题】:Overriding "defun" within a package 【发布时间】:2019-10-18 09:01:48 【问题描述】:

我想从我正在创建的包中定义一个名为“defun”的宏,并且我想将其导出以在某些地方使用。有一个名为 parenscript 的库可以在它的包中执行此操作,

(export #:defun)

当我尝试在自己的包中执行此操作时,我收到此 SB​​CL 错误

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:DEFUNCL:DEFUN

    如果您想编写不合格的DEFUN 符号并让读者找出引用了哪些符号,则可能会发生冲突。通常,库的用户可能会这样定义一个包:

    (defpackage :foo (:use :cl :p))
    

    这会导致冲突,因为“CL”和“P”包都导出“DEFUN”。解决这个问题的一种方法是定义一个重新绑定DEFUN 并从“CL”重新导出所有其他符号的 Common Lisp 方言。然后,您的用户必须只使用您的包而不是 CL 包。另一种方法是使用 CL 和 shadow-import 仅来自 P 的“DEFUN”,因此 DEFUNP: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”的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 在包中使用自定义 USER-Model

覆盖 npm 包依赖

如何在同一个包中隐藏类方法

Meteor 覆盖包中元素的点击事件

JAVA中如何定义自定义注解

Symfony - 如何覆盖不在捆绑包中的供应商组件