19.3 Using the Automatically Defined Constructor Function

After you have defined a new structure with defstruct, you can create instances of this structure by using the constructor function. By default, defstruct defines this function automatically. For a structure named foo, the constructor function is normally named make-foo; you can specify a different name by giving it as the argument to the :constructor option, or specify that you don’t want a normal constructor function at all by using nil as the argument (in which case one or more “by-position” constructors should be requested; see section 19.6).

A call to a constructor function, in general, has the form

(name-of-constructor-function
        slot-keyword-1 form-1
        slot-keyword-2 form-2
        ...)

All arguments are keyword arguments. Each slot-keyword should be a keyword whose name matches the name of a slot of the structure (defstruct determines the possible keywords simply by interning each slot-name in the keyword package). All the keywords and forms are evaluated. In short, it is just as if the constructor function took all its arguments as &key parameters. For example, the ship structure shown in section 19.1 has a constructor function that takes arguments roughly as if its definition were

(defun make-ship (&key x-position y-position
                       x-velocity y-velocity mass)
  ...)

If slot-keyword-j names a slot, then that element of the created structure will be initialized to the value of form-j. If no pair slot-keyword-j and form-j is present for a given slot, then the slot will be initialized by evaluating the default-init form specified for that slot in the call to defstruct. (In other words, the initialization specified in the defstruct defers to any specified in a call to the constructor function.) If the default initialization form is used, it is evaluated at construction time, but in the lexical environment of the defstruct form in which it appeared. If the defstruct itself also did not specify any initialization, the element’s initial value is undefined. You should always specify the initialization, either in the defstruct or in the call to the constructor function, if you care about the initial value of the slot.

Each initialization form specified for a defstruct component, when used by the constructor function for an otherwise unspecified component, is re-evaluated on every call to the constructor function. It is as if the initialization forms were used as init forms for the keyword parameters of the constructor function. For example, if the form (gensym) were used as an initialization form, either in the constructor-function call or as the default initialization form in the defstruct form, then every call to the constructor function would call gensym once to generate a new symbol.

X3J13 voted in October 1988 to clarify that the default value in a defstruct slot is not evaluated unless it is needed in the creation of a particular structure instance. If it is never needed, there can be no type-mismatch error, even if the type of the slot is specified, and no warning should be issued.

For example, in the following sequence only the last form is in error.

(defstruct person (name .007 :type string))

(make-person :name "James")

(make-person)     ;Error to give name the value .007