[Openmcl-devel] defstruct - generic constructor?

Gary Byers gb at clozure.com
Wed Apr 29 15:30:37 PDT 2009

On Wed, 29 Apr 2009, David Reitter wrote:

> I've been trying to make instances of objects whose types have been
> defined using `defstruct'.
> Unfortunately, the obvious `make-instance' doesn't work (with ccl at
> least).  I'm glad, however, that `class-of' and `class-name' do their
> jobs.

CLASS-OF is defined to return ... um, the CLASS of any lisp object:

? (class-of 1)
? (defstruct ship a b)
? (class-of (make-ship))
? (class-of *terminal-io*)

All of these classes happen to have non-null names, and the name of any
class can be accessed via the function CLASS-NAME.  This is just standard,
portable CL, and isn't really very remarkable.

The spec says that MAKE-INSTANCE is defined on STANDARD-CLASS (and on symbols
that name STANDARD-CLASSes); the spec (and the MOP) further say that MAKE-INSTANCE
is implemented in terms of ALLOCATE-INSTANCE and INITIALIZE-INSTANCE.

It generally doesn't make sense to use a portable object creation protocol to
"create instances" of built-in classes (in most implementations, it doesn't
make sense to talk about "allocating a FIXNUM"), and the MOP at least requires
that implementations define ALLOCATE-INSTANCE for build-in classes to signal
an error.

It certainly does make sense to use a portable object creation protocol to
allocate and initialize instances of (some subclass of) STANDARD-CLASS, and
an implementation has to support that protocol.

STRUCTURE-CLASSes are somewhere in between: implementations are neither required
to nor prohibited from making at least some parts of the object-creation protocol
work for structure objects as well as standard objects.  Some implementations support
at least using MAKE-INSTANCE to create structure instances, others don't, and there
may be some variance in some behaviors among implementations that do support it (e.g.,
are initarg names derived from slot names in the same way in all implementations ?)

CCL doesn't support using MAKE-INSTANCE to allocate and initialize
structures, though I mostly think that it should do so (as a non-portable extension),
mostly to make it easier to handle cases like the one that you describe.

> The only way I got this to work was this clumsy hack:
> (apply (read-from-string (format nil "make-~a" (class-name chunk-type)))
> ...)
> Is there a nicer, more efficient way of doing this?

More efficient (and more robust) ?  Sure.  Nicer ?  I dunno.  I don't
know of a portable way to do this; a couple of non-portable, CCL-specific
ways include:

1) if S is a STRUCTURE-CLASS, then:

? (allocate-instance S)

returns an instance of S in which all slots are initialized to NIL.

2) if S is a symbol that may name a structure class and ENV is a (possibly null)
lexical environment, then:

   (let* ((info (ccl::structure-class-p s env)))
     (when info
       (ccl::sd-constructor info)))

returns the name of the constructor function (if such a thing exists.)  Of course,
you don't know what arguments that function takes, or whether you can safely call
it with 0 arguments.

The fact that neither of these solutions is entirely satisfactory is an argument
in favor of making MAKE-INSTANCE work on structure classes.

> I'd need to find out what the constructor is for a given class.
> Reading the `defstruct'  code didn't help me much.
> In more general terms, can I expect that `class-of' and `class-name'
> work for structures in other CL implementations?
> Thanks for your input.
> _______________________________________________
> Openmcl-devel mailing list
> Openmcl-devel at clozure.com
> http://clozure.com/mailman/listinfo/openmcl-devel

More information about the Openmcl-devel mailing list