Common Lisp the Language, 2nd Edition

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

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

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: By-Position Constructor Functions Up: Structures Previous: Defstruct Slot-Options