SBCL:asdf:定义字符串常量时,load-system失败

Oll*_*aus 4 sbcl common-lisp

使用SBCL时,我遇到的问题是,当lisp代码定义一个字符串常量时,通过ASDF定义的系统不会加载.这是代码:

constants.lisp

(defconstant A 1.0)
(defconstant B "B")
Run Code Online (Sandbox Code Playgroud)

simple.asd

(defsystem :simple
:components ((:file "constants")))
Run Code Online (Sandbox Code Playgroud)

在加载时

(asdf:load-system "simple")
Run Code Online (Sandbox Code Playgroud)

我收到以下错误(输出已经缩短了一点):

* (asdf:load-system "simple")  
; compiling file "/Users/.../constants.lisp"
; compiling (DEFCONSTANT A ...)
; compiling (DEFCONSTANT B ...)
; /Users/.../constants-TMP.fasl written
; compilation finished in 0:00:00.003

debugger invoked on a DEFCONSTANT-UNEQL in thread
#<THREAD "main thread" RUNNING {1002BFEA93}>:
   The constant B is being redefined (from "B" to "B")
Run Code Online (Sandbox Code Playgroud)

clisp,ccl和abcl没有出现错误.另外,通过加载文件

(load "constants.lisp")
Run Code Online (Sandbox Code Playgroud)

工作良好.

我正在使用

SBCL 1.2.14,ASDF 3.1.3,MacOS

谢谢你的任何提示,

奥利弗

cor*_*ump 8

为什么字符串常量失败?

规范defconstant告诉我们:

但是,如果尝试使用另一个运算符为符号赋值,或者使用后续的defconstant将其分配给不同的值,则后果是未定义的.

这里重要的一点是不同的:根据哪个比较?

如果在执行defconstant存在由name命名的变量的任何绑定,或者该值不是eqlinitial-value的值,则结果是未定义的.

比较由eql完成.

SBCL编译您的文件然后加载结果(xxx-TMP.fasl文件),并且对于特定实现,defconstant因此在同一环境中对表单进行两次评估.编译器在编译期间不需要实际评估表单(它可以在内部以某种方式声明它,以便可以内联进一步使用常量),但这是一种有效的编译策略.

这里,由于编译环境和加载环境是相同的,并且因为字符串的两个出现相同(不是eq),所以发出错误信号.如果您碰巧使用相同版本的SBCL解释器的另一个实例加载FASL文件,它将不会给您这个错误.

你能做什么?

  1. 不要重新发明轮子,使用 alexandria:define-constant,这允许指定值在哪个测试函数不变.这是一个更复杂的版本:

    (defmacro defconst (symbol value)
     `(defconstant ,symbol 
        (or (and (boundp ',symbol) 
                 (symbol-value ',symbol))
            ,value)))
    
    Run Code Online (Sandbox Code Playgroud)

    在这里,扩展defconstant仍然是顶级形式,我们永远不会用不eql适合它的东西重新定义它.

  2. defvar在部署代码之前使用并且不再担心它(通常需要在开发期间更改常量).