Common Lisp the Language, 2nd Edition


next up previous contents index
Next: By-Position Constructor Functions Up: Structures Previous: Defstruct Slot-Options

19.5. Defstruct 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))

change_begin
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.)
change_end
: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).

change_begin
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).

change_end
These changes make the behavior of defstruct with respect to the :include option a bit more like the behavior of classes in CLOS.

: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 type symbol is a subtype of the specified element-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 up previous contents index
Next: By-Position Constructor Functions Up: Structures Previous: Defstruct Slot-Options


AI.Repository@cs.cmu.edu