2

I'm trying to write a small game in (SBCL) Common Lisp, using Quickload and ASDF to define and manage dependencies. It uses CLOS, so I have a directory in project called classes, and in there, a file, locatable.cl.

The defclass form for the LOCATABLE class needs a LOCATOR parameter, so I have a line:

:initform (error "Must supply a locator parameter for this class.")

Whenever I try to load this file or quickload the system, though, I get the error above ("Must supply a locator parameter for this class"). Since I'm trying to define a system and not creating any instances of the class, I don't understand why I'm getting this. If I comment out the error line, everything loads fine, but I was led to believe that the way I have it is a standard way of requiring an :initval for a slot.

How do you define such a thing so you can load the file/make a system definition without actually signaling the error?

Here's the class -

(defclass locatable ()
  ((zone
    :accessor zone
    :initform nil)
   (locator
    :initarg :locator
    :initform (error "Must supply a locator parameter for this class.")
    :allocation :class
    :accessor locator)))

UPDATE: I entered the form at the REPL and got the same error. For curiosity's sake, I entered it in again twice, first with :initform "", then with the error form. It accepted the first form, and didn't complain about the second, so this problem doesn't seem to happen on re-definition.

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
amp108
  • 432
  • 3
  • 14
  • Something in your system definition must be creating an instance without supplying the proper initialization arguments. – Barmar Mar 31 '15 at 00:24
  • 1
    If that line is by itself, at the toplevel of the file, it is going to evaluate the ERROR form at load time. Can you share the entire DEFCLASS form? – Xach Mar 31 '15 at 01:01
  • I've added the class definition to the main question, Xach, thanks. @Barmar, this happens in SLIME even if I only try to load 'locatable.cl', which has no `make-instance` call in it. – amp108 Mar 31 '15 at 17:55
  • It also seems to be okay when I leave out the `:allocation :class` line. I have no idea if this is because it has to do some kind of test for class-allocated fields, or because of something else. – amp108 Mar 31 '15 at 18:41
  • I suspect I might have to remove the `initform` and have any field-checking done in an `:after` method of `make-instance`, but I'm not sure. – amp108 Mar 31 '15 at 18:47
  • It seems like the shared slot is being initialized as soon as the class is defined, not when you create the first instance. I reproduced this in CLISP: http://ideone.com/vNCsh2. But I can't find anything in CLHS that supports this behavior. – Barmar Mar 31 '15 at 19:56

1 Answers1

1
(defclass locatable ()
  ((zone
    :accessor zone
    :initform nil)
   (locator
    :initarg :locator
    :initform (error "Must supply a locator parameter for this class.")
    :allocation :class
    :accessor locator)))

The slot locator is shared in the class. It will be allocated somehow in the class object. The DEFCLASS form creates this class object. Thus the slot locator usually will be initialized when the class object is created and initialized. Way before the first instance of that class is created.

LispWorks Backtrace

CL-USER 50 : 1 > :b
Call to CLOS::CLASS-REDEFINITION-LOCK-DEBUGGER-WRAPPER
Call to INVOKE-DEBUGGER
Call to ERROR
Call to (METHOD CLOS::COMPUTE-CLASS-SLOT-CONSES (STANDARD-CLASS))
Call to (METHOD SHARED-INITIALIZE :AFTER (STANDARD-CLASS T))    ; <--
Call to CLOS::ENSURE-CLASS-USING-CLASS-INTERNAL
Call to (METHOD CLOS:ENSURE-CLASS-USING-CLASS (CLASS T))
Call to CLOS::ENSURE-CLASS-WITHOUT-LOD
Call to LET
Call to LET
Call to EVAL
Call to CAPI::CAPI-TOP-LEVEL-FUNCTION
Call to CAPI::INTERACTIVE-PANE-TOP-LOOP
Call to MP::PROCESS-SG-FUNCTION

As you see SHARED-INITIALIZE is called on the class object, which then initializes the shared slots.

I also don't think calling error like this should be done in user code. You might find a better way to check for missing initargs.

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
  • Thanks. I figure I'll end up using an `:after` method on `initialize-instance`, or an `:initform` that supplies a default. – amp108 Mar 31 '15 at 20:48