Common Lisp the Language, 2nd Edition
Next: By-Position Constructor
Functions Up: Structures
Previous: Defstruct
Slot-Options
The preceding description of defstruct
is all that the
average user will need (or want) to know in order to use structures. The
remainder of this chapter discusses more complex features of the
defstruct
facility.
This section explains each of the options that can be given to
defstruct
. A defstruct
option may be either a
keyword or a list of a keyword and arguments for that keyword. (Note
that the syntax for defstruct
options differs from the pair
syntax used for slot-options. No part of any of these options is
evaluated.)
:conc-name
This provides for automatic prefixing of names of access functions. It
is conventional to begin the names of all the access functions of a
structure with a specific prefix, the name of the structure followed by
a hyphen. This is the default behavior.
The argument to the :conc-name
option specifies an
alternative prefix to be used. (If a hyphen is to be used as a
separator, it must be specified as part of the prefix.) If
nil
is specified as an argument, then no prefix is
used; then the names of the access functions are the same as the
slot-names, and it is up to the user to name the slots reasonably.
Note that no matter what is specified for :conc-name
,
with a constructor function one uses slot keywords that match the
slot-names, with no prefix attached. On the other hand, one uses the
access-function name when using setf
. Here is an
example:
(defstruct door knob-color width material)
(setq my-door
(make-door :knob-color 'red :width 5.0))
(door-width my-door) => 5.0
(setf (door-width my-door) 43.7)
(door-width my-door) => 43.7
(door-knob-color my-door) => red
:constructor
This option takes one argument, a symbol, which specifies the name of
the constructor function. If the argument is not provided or if the
option itself is not provided, the name of the constructor is produced
by concatenating the string "MAKE-"
and the name of the
structure, putting the name in whatever package is current at the time
the defstruct
form is processed (see
*package*
). If the argument is provided and is
nil
, no constructor function is defined.
This option actually has a more general syntax that is explained in section 19.6.
:copier
This option takes one argument, a symbol, which specifies the name of
the copier function. If the argument is not provided or if the option
itself is not provided, the name of the copier is produced by
concatenating the string "COPY-"
and the name of the
structure, putting the name in whatever package is current at the time
the defstruct
form is processed (see
*package*
). If the argument is provided and is
nil
, no copier function is defined.
The automatically defined copier function simply makes a new
structure and transfers all components verbatim from the argument into
the newly created structure. No attempt is made to make copies of the
components. Corresponding components of the old and new structures will
therefore be eql
.
:predicate
This option takes one argument, which specifies the name of the type
predicate. If the argument is not provided or if the option itself is
not provided, the name of the predicate is made by concatenating the
name of the structure to the string "-P"
, putting the name
in whatever package is current at the time the defstruct
form is processed (see *package*
). If the argument is
provided and is nil
, no predicate is defined. A predicate
can be defined only if the structure is ``named’’; if the
:type
option is specified and the :named
option is not specified, then the :predicate
option must
either be unspecified or have the value nil
.
:include
This option is used for building a new structure definition as an
extension of an old structure definition. As an example, suppose you
have a structure called person
that looks like this:
(defstruct person name age sex)
Now suppose you want to make a new structure to represent an
astronaut. Since astronauts are people too, you would like them also to
have the attributes of name, age, and sex, and you would like Lisp
functions that operate on person
structures to operate just
as well on astronaut
structures. You can do this by
defining astronaut
with the :include
option,
as follows:
(defstruct (astronaut (:include person)
(:conc-name astro-))
helmet-size
(favorite-beverage 'tang))
The :include
option causes the structure being defined
to have the same slots as the included structure. This is done in such a
way that the access functions for the included structure will also work
on the structure being defined. In this example, an
astronaut
will therefore have five slots: the three defined
in person
and the two defined in astronaut
itself. The access functions defined by the person
structure can be applied to instances of the astronaut
structure, and they will work correctly. Moreover,
astronaut
will have its own access functions for components
defined by the person
structure. The following examples
illustrate how you can use astronaut
structures:
(setq x (make-astronaut :name 'buzz
:age 45
:sex t
:helmet-size 17.5))
(person-name x) => buzz
(astro-name x) => buzz
(astro-favorite-beverage x) => tang
The difference between the access functions person-name
and astro-name
is that person-name
may be
correctly applied to any person
, including an
astronaut
, while astro-name
may be correctly
applied only to an astronaut
. (An implementation may or may
not check for incorrect use of access functions.)
At most one :include
option may be specified in a single
defstruct
form. The argument to the :include
option is required and must be the name of some previously defined
structure. If the structure being defined has no :type
option, then the included structure must also have had no
:type
option specified for it. If the structure being
defined has a :type
option, then the included structure
must have been declared with a :type
option specifying the
same representation type.
If no :type
option is involved, then the structure name
of the including structure definition becomes the name of a data type,
of course, and therefore a valid type specifier recognizable by
typep
; moreover, it becomes a subtype of the included
structure. In the above example, astronaut
is a subtype of
person
; hence
(typep (make-astronaut) 'person)
is true, indicating that all operations on persons will also work on astronauts.
The following is an advanced feature of the :include
option. Sometimes, when one structure includes another, the default
values or slot-options for the slots that came from the included
structure are not what you want. The new structure can specify default
values or slot-options for the included slots different from those the
included structure specifies, by giving the :include
option
as
(:include name slot-description-1 slot-description-2 ...)
Each slot-description-j must have a slot-name or slot-keyword that is the same as that of some slot in the included structure. If slot-description-j has no default-init, then in the new structure the slot will have no initial value. Otherwise its initial value form will be replaced by the default-init in slot-description-j. A normally writable slot may be made read-only. If a slot is read-only in the included structure, then it must also be so in the including structure. If a type is specified for a slot, it must be the same as, or a subtype of, the type specified in the included structure. If it is a strict subtype, the implementation may or may not choose to error-check assignments.
For example, if we had wanted to define astronaut
so
that the default age for an astronaut is 45
, then we could
have said:
(defstruct (astronaut (:include person (age 45)))
helmet-size
(favorite-beverage 'tang))
X3J13 voted in June 1988 (DATA-TYPES-HIERARCHY-UNDERSPECIFIED) to
require any structure type created by defstruct
(or
defclass
) to be disjoint from any of the types
cons
, symbol
, array
,
number
, character
, hash-table
,
readtable
, package
, pathname
,
stream
, and random-state
. A consequence of
this requirement is that it is an error to specify any of these types,
or any of their subtypes, to the defstruct
:include
option. (The first edition said nothing explicitly
about this. Inasmuch as using such a type with the :include
option was not defined to work, one might argue that such use was an
error in Common Lisp as defined by the first edition.)
:print-function
This option may be used only if the :type
option is not
specified. The argument to the :print-function
option
should be a function of three arguments, in a form acceptable to the
function
special form, to be used to print structures of
this type. When a structure of this type is to be printed, the function
is called on three arguments: the structure to be printed, a stream to
print to, and an integer indicating the current depth (to be compared
against *print-level*
). The printing function should
observe the values of such printer-control variables as
*print-escape*
and *print-pretty*
.
If the :print-function
option is not specified and the
:type
option also not specified, then a default printing
function is provided for the structure that will print out all its slots
using #S
syntax (see section 22.1.4).
X3J13 voted in January 1989 (PRINT-CIRCLE-STRUCTURE) to specify
that user-defined printing functions for the defstruct
:print-function
option may print objects to the supplied
stream using write
, print1
,
princ
, format
, or print-object
and expect circularities to be detected and printed using
#
n
#
syntax (when
*print-circle*
is non-nil
, of course). See
*print-circle*
.
X3J13 voted in January 1989 (DEFSTRUCT-PRINT-FUNCTION-INHERITANCE)
to clarify that if the :print-function
option is not
specified but the :include
option is specified,
then the print function is inherited from the included structure type.
Thus, for example, an astronaut
will be printed by the same
printing function that is used for person
.
X3J13 in the same vote extended the print-function
option as follows: If the print-function
option is
specified but with no argument, then the standard default printing
function (that uses #S
syntax) will be used. This provides
a means of overriding the inheritance rule. For example, if
person
and astronaut
had been defined as
(defstruct (person
(:print-function ;Special print function
(lambda (p s k)
(format s "<~A, age ~D>"
(person-name p)
(person-age p)))))
name age sex)
(defstruct (astronaut
(:include person)
(:conc-name astro-)
(:print-function)) ;Use default print function
helmet-size
(favorite-beverage 'tang))
then an ordinary person would be printed as
``<Joe Schmoe, age 27>
’’ but an astronaut would be
printed as, for example,
#S(ASTRONAUT NAME BUZZ AGE 45 SEX T
HELMET-SIZE 17.5 FAVORITE-BEVERAGE TANG)
using the default #S
syntax (yuk).
:type
The :type
option explicitly specifies the representation to
be used for the structure. It takes one argument, which must be one of
the types enumerated below.
Specifying this option has the effect of forcing a specific
representation and of forcing the components to be stored in the order
specified in the defstruct
form in corresponding successive
elements of the specified representation. It also prevents the
structure name from becoming a valid type specifier recognizable by
typep
(see section 19.7).
Normally this option is not specified, in which case the structure is represented in an implementation-dependent manner.
vector
This produces the same result as specifying(vector t)
. The structure is represented as a general vector, storing components as vector elements. The first component is vector element 1 if the structure is:named
, and element 0 otherwise.
(vector
element-type
)
The structure is represented as a (possibly specialized) vector, storing components as vector elements. Every component must be of a type that can be stored in a vector of the type specified. The first component is vector element 1 if the structure is:named
, and element 0 otherwise. The structure may be:named
only if the typesymbol
is a subtype of the specifiedelement-type
.
list
The structure is represented as a list. The first component is the cadr if the structure is:named
, and the car if it is:unnamed
.
:named
The :named
option specifies that the structure is
``named’’; this option takes no argument. If no :type
option is specified, then the structure is always named; so this option
is useful only in conjunction with the :type
option. See
section 19.7 for a
further description of this option.
:initial-offset
This allows you to tell defstruct
to skip over a certain
number of slots before it starts allocating the slots described in the
body. This option requires an argument, a non-negative integer, which is
the number of slots you want defstruct
to skip. The
:initial-offset
option may be used only if the
:type
option is also specified. See section 19.7.3 for a further
description of this option.
Next: By-Position Constructor
Functions Up: Structures
Previous: Defstruct
Slot-Options
AI.Repository@cs.cmu.edu