Common Lisp the Language, 2nd Edition
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.)
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
This option actually has a more general syntax that is explained in section 19.6.
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.
(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))
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 (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).
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.
- 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 type symbol is a subtype of the specified element-type.
- 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.