6
votes

Using SBCL, I have the problem that my system defined via ASDF does not load, when the lisp code defines a string constant. Here's the code:

constants.lisp

(defconstant A 1.0)
(defconstant B "B")

simple.asd

(defsystem :simple
:components ((:file "constants")))

On loading via

(asdf:load-system "simple")

I'm getting the following error (output has been shortened a bit):

* (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")

The error does not come up with clisp, ccl and abcl. Also, loading the file via

(load "constants.lisp")

works fine.

I'm using

SBCL 1.2.14, ASDF 3.1.3, MacOS

Thanks for any hints,

Oliver

2
Note that this problem is independent of ASDF. It already happens if your evaluate (defconstant B "B") twice in the sbcl repl.Rainer Joswig

2 Answers

10
votes

Why does the string constant fails?

The specification for defconstant tells us that:

However, the consequences are undefined if an attempt is made to assign a value to the symbol using another operator, or to assign it to a different value using a subsequent defconstant.

The important word here is different: according to which comparison?

The consequences are undefined if there are any bindings of the variable named by name at the time defconstant is executed or if the value is not eql to the value of initial-value.

The comparison is done by eql.

SBCL compiles your file and then loads the result (the xxx-TMP.fasl file), and for that particular implementation, the defconstant form is thus evaluated twice in the same environment. A compiler needs not actually evaluate the form during compilation (it could declare it internally somehow so that further uses of the constants can be inlined) but this is a valid compilation strategy.

Here, since the compilation environment and the load environment are the same, and because the two occurences of the strings are not identical (not eq), an error is signaled. If you happened to load the FASL file with another instance of the same version of the SBCL interpreter, it would not give you this error.

What can you do?

  1. Don't reinvent the wheel, use alexandria:define-constant, which allows to specify under which test function the value is constant:

    (alexandria:define-constant b "B" :test #'string=)
    

    Thus, when it is evaluated multiple times, the new "B" value is compared against the existing value using string=, and since they are equal, no other action is performed.

  2. Use defvar and do not worry about it anymore until you deploy your code (often constants need to change during development).

0
votes

Thanks to all of you. Switched over to alexandria.