Common Lisp 实现之间不同的“:cl”包处理

different ":cl" package handling between Common Lisp implementations

为什么会发生这种行为? 此外,这是 "implementation defined" 差异,还是其中一个 REPL 错误?

请考虑这个 Common Lisp 代码...

(defpackage :new)
(in-package new)
(+ 2 2)

在 CMUCL 中,这计算为数字四。

在 SBCL 中,这个 returns returns 一个错误:

; in: + 2
;     (NEW::+ 2 2)
;
; caught COMMON-LISP:STYLE-WARNING:
;   undefined function: NEW::+
;
; compilation unit finished
;   Undefined function:
;     +
;   caught 1 STYLE-WARNING condition

debugger invoked on a COMMON-LISP:UNDEFINED-FUNCTION in thread
#<THREAD "main thread" RUNNING {1000508083}>:
  The function NEW::+ is undefined.

但是,SBCL 会在计算为

时正确调用“+”

(cl:+ 2 2)

CMUCL 也适用于此。

当我查看 HyperSpec 时,我无法找到解决此上下文的明确部分。我能找到的最接近的是:Section 11.1.2.2 The COMMON-LISP-USER Package。这让我相信 SBCL 的解释是正确的; "NEW" 没有从 "COMMON-LISP" 继承符号,因此无法从 "NEW" 访问整个 Common Lisp 语言。 但是三行就把整个语言都干掉了,所以我还是不清楚。

您可以使用:use指定从defpackage中继承符号的包,或者您可以调用use-package达到相同的目的;我通常在 defpackage 内完成。我不知道 CMUCL 在这方面的表现与 SBCL 不同,但我总是在我的包中包含 (:use :common-lisp)。 Common Lisp HyperSpec defpackage 文档说:If :use is not supplied, it defaults to the same implementation-dependent value as the :use argument to make-package. 在这里您可以看到 CMUCL 和 SBCL 之间的区别:

CMUCL


CL-USER> ;; CMUCL
; No value
CL-USER> (defpackage :new)
#<The NEW package, 0/9 internal, 0/2 external>
CL-USER> (package-use-list :new)
(#<The COMMON-LISP package, 0/6 internal, 978/1227 external>)

SBCL


CL-USER> ;; SBCL
; No value
CL-USER> (defpackage :new)
#<PACKAGE "NEW">
CL-USER> (package-use-list :new)
NIL

package-use-list 函数显示 new 包在 CMUCL 中使用 common-lisp,但在 SBCL 中没有。您可以通过显式 useing 包来避免此类问题:

CL-USER> (defpackage :new (:use :common-lisp))
#<PACKAGE "NEW">
CL-USER> (package-use-list :new)
(#<PACKAGE "COMMON-LISP">)
CL-USER> (in-package :new)
#<PACKAGE "NEW">
NEW> (+ 2 2)
4

通常在文件中使用 defpackage 形式的 :export 来导出符号,但是从 new 包中的 REPL 您也可以定义函数并导出他们在其他包中使用的符号:

NEW> (defun add3 (x) (+ x 3))
ADD3
NEW> (export 'add3)
T

然后回到主工作区,common-lisp-user包,调用use-package访问新函数:

NEW> (in-package :cl-user)
#<PACKAGE "COMMON-LISP-USER">
CL-USER> (use-package :new)
T
CL-USER> (add3 4)
7

当 DEFPACKAGE 中没有 :use 子句时,使用 打包的 CL 标准未定义。

CLHS:DEFPACKAGE

The arguments to :use set the packages that the package named by package-name will inherit from. If :use is not supplied, it defaults to the same implementation-dependent value as the :use argument to make-package.

SBCL 选择不使用 任何包。

传统上其他 CL 实现通常选择使用 CL 包加上一些扩展包。当时的意图是,默认情况下,新包对 Lisp 编程 有用 ,例如包 CL-USER.

对于可移植代码,您需要指定一个包应该使用哪些包。通常 defpackagemake-package 是要查看的运算符。

这是对其他答案的补充。

我过去做过的一件事是写一个这样的宏:

(defvar *user-package-use-list*
  (package-use-list (find-package ':common-lisp-user)))

(defmacro define-user-package (name &body options)
  `(defpackage ,name
     (:use ,@(mapcar #'package-name *user-package-use-list*))
     ,@options))

然后使用这个

(define-user-package :my-user-package)

将定义一个 'like' CL-USER 的包,因为它使用所有相同的包,并且它将以跨实现工作的方式执行此操作。