[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

various DEFSTRUCT issues



At the X3J13 meeting there was an issue about DEFSTRUCT and duplicated
slot names.  This has caused me to think a bit about some other related
problems with DEFSTRUCT.  I haven't really given a whole lot of thought
to these, but I wanted to get them out to some other people before I
forgot.

Issue 1: Slots with STRING-EQUAL names

This is the one that I briefly mentioned at the meeting.  Consider the
following:

(in-package 'foo)
(defstruct struct
  slot1
  bar:slot1)

The problem with this is that the package of the slot name is generally
not used by DEFSTRUCT.  The constructor takes keyword arguments, so it
would be ambiguous which slot is being initialized by (make-struct
:slot1 <val>).  And accessor functions are interned in the package that
is current when the DEFSTRUCT is being expanded, so it would try to
define FOO::STRUCT-SLOT1 as an accessor for both slots.

In the case of a single structure definition, like above, it would
probably be OK to specify that string-equal slot names are not
permitted.  However, they are more likely to occur when :INCLUDEing a
structure in a different package:

(in-package 'foo)
(defstruct foo-struct
  slot1)

and in a different file:

(in-package 'bar)
(defstruct (bar-struct (:include foo:foo-struct))
  slot1)

In this case you don't have a problem with the accessors, because they
are FOO::FOO-STRUCT-SLOT1 and BAR::BAR-STRUCT-SLOT1.  However, you still
have a problem with MAKE-BAR-STRUCT, because of the keyword argument.

I think the solution to this is to make use of the extension to keyword
argument syntax that was done for CLOS.  The keyword arguments to
structure constructors would be the actual name of the argument.
However, for back compatibility, we can also specify that :keywords are
also accepted (but we should deprecate this use).  However, it is an error
to use the :keyword version if the structure contains two string-equal
slot names.

Additionally, the specification of the default printed representation of
structures must be tightened, as it currently allows the slot names to
be printed in any package.  They should be printed with their correct
package prefixes.

Existing practice: Sun Common Lisp 2.0.3 (Lucid, I believe) detects
attempts to define structures with string-equal slots at defstruct
expansion time, whether the conflicting slot comes from inheritance or
not.  Symbolics Common Lisp in Genera 7.2 and Kyoto Common Lisp June 3,
1987 don't notice the conflict.  In SCL, (make-bar-struct :slot1 <val>)
creates a structure in which both slots contain <val>); in KCL, the
FOO::SLOT1 slot contains VAL, while the BAR::SLOT1 slot contains NIL.
Regarding printed reps, Symbolics currently prints all the slot names as
:keywords; KCL simply PRIN1's the slot name symbols, so it corresponds
to my proposal; Sun CL uses no package prefixes at all.

Issue 2: Redefining structures

CLOS specifies in greate detail what happens when classes are redefined.
What happens when structures are redefined?  Does CLtL discuss this
anywhere?  Assuming existing practice is what I think it is, we should
probably specify that it is an error to use a structure accessor or
copier on a structure that was created prior to the redefinition; this
may imply that it is an error to try to print such a structure, as the
print function might use an accessor.

But what about redefining :INCLUDEd structures?  What does this do:

(defstruct foo
  a b)

(defstruct (bar (:include foo))
  c)

(defstruct foo
  b a d)

How does this redefinition of FOO affect the BAR structure?  In the very
least, I think my above statement should be taken to imply that any BARs
created before FOO is redefined are no longer accessible using the FOO-
accessors.  But what about new structures created by MAKE-BAR after the
redefinition?  In all three implementations I tried, the redefinition
had absolutely no effect on MAKE-BAR, nor on the printing of BAR
structures.  The only anomoly is that (foo-b (make-bar :a 1 :b 2)) now
returns 1; all three apparently implement structures internally as
vectors, and accessors are simply AREFs.  One could imagine, however, an
implementation that used property lists or alists internally, so that
FOO-B would continue to return the B slot of BAR structures.

So, we could say that after redefining a structure, it is an error to
use any of its accessors on structures that :INCLUDE that structure but
which have not been reevaluated/recompiled.  (Actually, that wording is
pretty attrocious.)  And after the :INCLUDEing structure is redefined,
the :INCLUDEe's accessors may only be used on new instances.

                                                barmar