.c -*-Bolio-*- .chapter "Defstruct" .section "Introduction" The features of defstruct differ slightly from one Lisp implementation to another. However, defstruct makes it fairly easy to write compatible code if the user doesn't try to exercise any of the more esoteric features of his particular Lisp implementation. The differences will be pointed out as they occur. One difference that we must deal with immediately is the question of packages. defstruct makes use of a large number of keywords, and on the Lisp Machine those keywords are all interned on the keyword package. However, for the purposes of compatibility, the Lisp Machine defstruct will allow the keywords to appear in any package. The Lisp Machine programmer is discouraged from writing keywords without colons, unless the code is to be transported to another Lisp implementation. Classes of symbols that defstruct treats as keywords will be noted as they occur. Other package related issues will be dealt with later. .section "A Simple Example" .defmac defstruct defstruct is a macro defining macro. The best way to explain how it works is to show a sample call to defstruct, and then to show what macros are defined and what each of them does. Sample call to defstruct: (defstruct (elephant (type list)) color (size 17.) (name (gensym))) This form expands into a whole rat's nest of stuff, but the effect is to define five macros: color, size, name, make-elephant and alter-elephant. Note that there were no symbols make-elephant or alter-elephant in the original form, they were created by defstruct. The definitions of color, size and name are easy, they expand as follows: (color x) ==> (car x) (size x) ==> (cadr x) (name x) ==> (caddr x) You can see that defstruct has decided to implement an elephant as a list of three things; its color, its size and its name. The expansion of make-elephant is somewhat harder to explain, let's look at a few cases: (make-elephant) ==> (list nil 17. (gensym)) (make-elephant color 'pink) ==> (list 'pink 17. (gensym)) (make-elephant name 'fred size 100) ==> (list nil 100 'fred) As you can see, make-elephant takes a "setq-style" list of part names and forms, and expands into a call to list that constructs such an elephant. Note that the unspecified parts get defaulted to pieces of code specified in the original call to defstruct. Note also that the order of the setq-style arguments is ignored in constructing the call to list. (In the example, 100 is evaluated before 'fred even though 'fred came first in the make-elephant form.) Care should thus be taken in using code with side effects within the scope of a make-elephant. Finally, take note of the fact that the (gensym) is evaluated \every time/ a new elephant is created (unless you override it). The explanation of what alter-elephant does is delayed until (alterant-section-page). So now you know how to construct a new elephant and how to examine the parts of an elephant, but how do you change the parts of an already existing elephant? The answer is to use the setf macro ((setf-section-page)). (setf (name x) 'bill) ==> (rplaca (cddr x) 'bill) which is what you want. And that is just about all there is to defstruct; you now know enough to use it in your code, but if you want to know about all its interesting features, then read on. .section "Syntax of defstruct" The general form of a defstruct form is: (defstruct ( ... ) ... ) must be a symbol, it is used in constructing names (such as "make-elephant") and it is given a defstruct-description property of a structure that describes the structure completely. Each is either the atomic name of an option, or a list of the form ( . ). Some options have defaults for ; some will complain if they are present without an argument; some options complain if they are present \with/ an argument. The interpretation of is up to the option in question, but usually it is expected to be nil. Each is either the atomic name of a slot in the structure, or a list of the form ( ), or a list of byte field specifications. is used by constructor macros (such as make-elephant) to initialize slots not specified in the call to the constructor. If the is not specified, then the slot is initialized to whatever is most convenient. (In the elephant example, since the structure was a list, nil was used. If the structure had been a fixnum array, such slots would be filled with zeros.) A byte field specification looks like: ( ) or ( ). Note that since a byte field specification is always a list, a list of byte field specifications can never be confused with the other cases of a slot description. The byte field feature of defstruct is explained in detail in (byte-field-section-page). .section "Options to defstruct" The following sections document each of the options defstruct understands in detail. On the Lisp Machine, all these defstruct options are interned on the keyword package. .subsection "type" The type option specifies what kind of lisp object defstruct is going to use to implement your structure, and how that implementation is going to be carried out. The type option is illegal without an argument. If the type option is not specified, then defstruct will choose an appropriate default (hunks on PDP-10s, arrays on Lisp Machines and lists on Multics). It is possible for the user to teach defstruct new ways to implement structures, the interested reader is referred to (extension-section-page), for more information. Many useful types have already been defined for the user. A table of these "built in" types follows: (On the Lisp Machine all defstruct types are interned on the keyword package.) .defstruct_type list "All implementations" Uses a list. This is the default on Multics. .defstruct_type named-list "All implementations" Like list, except the car of each instance of this structure will be the name symbol of the structure. This is the only "named" structure type defined on Multics. (See the named option documented in (named-section-page).) .defstruct_type tree "All implementations" Creates a binary tree out of conses with the slots as leaves. The theory is to reduce car-cdring to a minimum. The include option ((include-section-page)) does not work with structures of this type. .defstruct_type list* "All implementations" Similar to list, but the last slot in the structure will be placed in the cdr of the final cons of the list. Some people call objects of this type "dotted lists". The include option ((include-section-page)) does not work with structures of this type. .defstruct_type array "All implementations" Uses an array object (\not/ a symbol with an array property). This is the default on Lisp Machines. Lisp Machine users may want to see the make-array option documented in (make-array-section-page). .defstruct_type fixnum-array "All implementations" Like array, except it uses a fixnum array and thus your structure can only contain fixnums. On Lisp Machines defstruct uses an art-32b type array for this type. .defstruct_type flonum-array "All implementations" Analogous to fixnum-array. On Lisp Machines defstruct uses an art-float type array for this type. .defstruct_type un-gc-array "PDP-10 only" Uses a nil type array instead of a t type. Note that this type does not exist on Lisp Machines or Multics, because un-garbage-collected arrays do not work in those implementations. .defstruct_type hunk "PDP-10 only" Uses a hunk. This is the default on PDP-10s. .defstruct_type named-hunk "PDP-10 only" Like hunk, except the car of each instance of this structure will be the name symbol of the structure. This \can/ be used with the (status usrhunk) feature of PDP-10 Maclisp to give the user Lisp Machine-like named structures. (See the named option documented in (named-section-page).) .defstruct_type sfa "PDP-10 only" Uses an SFA. The constructor macros for this type accept the keywords sfa-function and sfa-name. Their arguments (evaluated, of course) are used, respectively, as the function and the printed representation of the SFA. See also the sfa-function ((sfa-function-section-page)) and sfa-name ((sfa-name-section-page)) options. .defstruct_type vector "PDP10 and NIL (someday soon)" Uses an vector. .defstruct_type extend "PDP10 and NIL (someday soon)" Uses an extend. extends behave much more like user-created data types than any other defstruct type available in PDP10 MacLisp. The function struct-typep returns the structure name as its "type", and is null on all but extend structures. A minimal "class" system stands behind these structures, with various system operations (such as print, subst, etc) having reasonable default methods provided. For example, by supplying specialized print methods, one can define structures with many circular links, but which print in some abbreviated format. Also, during interpretation, the selector macros check to see that they are being applied to the right kind of structure. This is currently implemented as an interface to defvst so that you can get the union of the features of defstruct and defvst. See documentation DEFVST.DOC (on ITS, .INFO.;LISP DEFVST) and also EXTEND.DOC (on ITS, .INFO.;LISP EXTEND), especially with regard to defining new methods. .defstruct_type named-array "Lisp Machine only" Uses an array with the named structure bit set and stores the name symbol of the structure in the first element. (See the make-array option documented in (make-array-section-page).) .defstruct_type array-leader "Lisp Machine only" Uses an array with a leader. (See the make-array option documented in (make-array-section-page).) .defstruct_type named-array-leader "Lisp Machine only" Uses an array with a leader, sets the named structure bit, and stores the name symbol in element 1 of the leader. (See the make-array option documented in (make-array-section-page).) .defstruct_type fixnum "All implementations" This type allows one to use the byte field feature of defstruct to deal symbolically with fixnums that aren't actually stored in any structure at all. Essentially, a structure of type fixnum has exactly one slot. This allows the operation of retrieving the contents of that slot to be optimized away into the identity operation. See (byte-field-section-page) for more information about byte fields. .defstruct_type external "Multics only" Uses an array of type external (only Multics Lisp has these). Constructor macros for structures of this kind take the external-ptr keyword to tell them where the array is to be allocated. (See (constructor-section-page), for an explanation of constructor macro keywords.) See also the external-ptr option described in (external-ptr-section-page). .subsection "constructor" The constructor option specifies the name to be given to the constructor macro. Without an argument, or if the option is not present, the name defaults to the concatenation of "make-" with the name of the structure. If the option is given with an argument of nil, then no constructor is defined. Otherwise the argument is the name of the constructor to define. Normally the syntax of the constructor defstruct defines is: ( ... ) Each must be the name of a slot in the structure (not necessarily the name of an accessor macro; see the conc-name option, (conc-name-section-page)), or one of the special keywords allowed for the particular type of structure being constructed. For each keyword that is the name of a slot, the constructor expands into code to make an instance of the structure using to initialize slot . Unspecified slots default to the forms given in the original defstruct form, or, if none was given there, to some convenient value such as nil or 0. For keywords that are not names of slots, the use of the corresponding code varies. Usually it controls some aspect of the instance being constructed that is not otherwise constrained. See, for example, the make-array option ((make-array-section-page)), the sfa-function option ((sfa-function-section-page), or the external-ptr option ((external-ptr-section-page)). On the Lisp Machine all such constructor macro keywords (those that are \not/ the names of slots) are interned on the keyword package. If the constructor option is given as (constructor ), then instead of making a keyword driven constructor, defstruct defines a "function style" constructor. The is used to describe what the arguments to the constructor will be. In the simplest case something like (constructor make-foo (a b c)) defines make-foo to be a three argument constructor macro whose arguments are used to initialize the slots named a, b and c. In addition, the keywords &optional, &rest and &aux are recognized in the argument list. They work in the way you might expect, but there are a few fine points worthy of explanation: (constructor make-foo (a &optional b (c 'sea) &rest d &aux e (f 'eff))) This defines make-foo to be a constructor of one or more arguments. The first argument is used to initialize the a slot. The second argument is used to initialize the b slot. If there isn't any second argument, then the default value given in the body of the defstruct (if given) is used instead. The third argument is used to initialize the c slot. If there isn't any third argument, then the symbol sea is used instead. The arguments from the fourth one on are collected into a list and used to initialize the d slot. If there are three or less arguments, then nil is placed in the d slot. The e slot is \not initialized/. It's value will be something convenient like nil or 0. And finally the f slot is initialized to contain the symbol eff. The b and e cases were carefully chosen to allow the user to specify all possible behaviors. Note that the &aux "variables" can be used to completely override the default initializations given in the body. Since there is so much freedom in defining constructors this way, it would be cruel to only allow the constructor option to be given once. So, by special dispensation, you are allowed to give the constructor option more than once, so that you can define several different constructors, each with a different syntax. Note that even these "function style" constructors do not guarantee that their arguments will be evaluated in the order that you wrote them. .subsection "alterant" The alterant option defines a macro that can be used to change the value of several slots in a structure together. Without an argument, or if the option is not present, the name of the alterant macro defaults to the concatenation of "alter-" with the name of the structure. If the option is given with an argument of nil, then no alterant is defined. Otherwise the argument is the name of the alterant to define. The syntax of the alterant macro defstruct defines is: ( ... ) should evaluate to an instance of the structure, each is evaluated and the result is made to be the value of slot of that structure. The slots are all altered in parallel after all code has been evaluated. (Thus you can use an alterant macro to exchange the contents to two slots.) Example: (defstruct (lisp-hacker (type list) conc-name default-pointer alterant) (favorite-macro-package nil) (unhappy? t) (number-of-friends 0)) (setq lisp-hacker (make-lisp-hacker)) Now we can perform a transformation: (alter-lisp-hacker lisp-hacker favorite-macro-package 'defstruct number-of-friends 23. unhappy? nil) ==> ((lambda (G0009) ((lambda (G0011 G0010) (setf (car G0009) 'defstruct) (setf (caddr G0009) G0011) (setf (cadr G0009) G0010)) 23. nil)) lisp-hacker) Although it appears from this example that your forms will be evaluated in the order in which you wrote them, this is not guaranteed. Alterant macros are particularly good at simultaneously modifying several byte fields that are allocated from the same word. They produce better code than you can by simply writing consecutive setfs. They also produce better code when modifying several slots of a structure that uses the but-first option ((but-first-section-page)). .subsection "default-pointer" Normally the accessors are defined to be macros of exactly one argument. (They check!) But if the default-pointer option is present then they will accept zero or one argument. When used with one argument, they behave as before, but given no arguments, they expand as if they had been called on the argument to the default-pointer option. An example is probably called for: (defstruct (room (type tree) (default-pointer **current-room**)) (room-name 'y2) (room-contents-list nil)) Now the accessors expand as follows: (room-name x) ==> (car x) (room-name) ==> (car **current-room**) If no argument is given to the default-pointer option, then the name of the structure is used as the "default pointer". default-pointer is most often used in this fashion. .subsection "conc-name" Frequently all the accessor macros of a structure will want to have names that begin the same way; usually with the name of the structure followed by a dash. The conc-name option allows the user to specify this prefix. Its argument should be a symbol whose print name will be concatenated onto the front of the slot names when forming the accessor macro names. If the argument is not given, then the name of the structure followed by a dash is used. If the conc-name option is not present, then no prefix is used. An example illustrates a common use of the conc-name option along with the default-pointer option: (defstruct (location default-pointer conc-name) (x 0) (y 0) (z 0)) Now if you say (setq location (make-location x 1 y 34 z 5)) it will be the case that (location-y) will return 34. Note well that the name of the slot ("y") and the name of the accessor macro for that slot ("location-y") are different. .subsection "include" The include option inserts the definition of its argument at the head of the new structure's definition. In other words, the first slots of the new structure are equivalent to (i.e. have the same names as, have the same inits as, etc.) the slots of the argument to the include option. The argument to the include option must be the name of a previously defined structure of the same type as the new one. If no type is specified in the new structure, then it is defaulted to that of the included one. It is an error for the include option to be present without an argument. Note that include does not work on certain types of structures (e.g. structures of type tree or list*). Note also that the conc-name, default-pointer, but-first and callable-accessors options only apply to the accessors defined in the current defstruct; no new accessors are defined for the included slots. An example: (defstruct (person (type list) conc-name) name age sex) (defstruct (spaceman (include person) default-pointer) helmet-size (favorite-beverage 'tang)) Now we can make a spaceman like this: (setq spaceman (make-spaceman name 'buzz age 45. sex t helmet-size 17.5)) To find out interesting things about spacemen: (helmet-size) ==> (cadddr spaceman) (person-name spaceman) ==> (car spaceman) (favorite-beverage x) ==> (car (cddddr x)) As you can see the accessors defined for the person structure have names that start with "person-" and they only take one argument. The names of the accessors for the last two slots of the spaceman structure are the same as the slot names, but they allow their argument to be omitted. The accessors for the first three slots of the spaceman structure are the same as the accessors for the person structure. Often, when one structure includes another, the default initial values supplied by the included structure will be undesirable. These default initial values can be modified at the time of inclusion by giving the include option as: (include ... ) Each is either the name of an included slot or of the form ( ). If it is just a slot name, then in the new structure (the one doing the including) that slot will have no initial value. If a new initial value is given, then that code replaces the old initial value code for that slot in the new structure. The included structure is unmodified. .subsection "named" This option tells defstruct that you desire your structure to be a "named structure". On PDP-10s this means you want your structure implemented with a named-hunk or named-list. On a Lisp Machine this indicates that you desire either a named-array or a named-array-leader or a named-list. On Multics this indicates that you desire a named-list. defstruct bases its decision as to what named type to use on whatever value you did or didn't give to the type option. It is an error to use this option with an argument. .subsection "make-array" Available only on Lisp Machines, this option allows the user to control those aspects of the array used to implement the structure that are not otherwise constrained by defstruct (such as the area it is to be allocated in). The argument to the make-array option should be a list of alternating keyword symbols to the Lisp Machine make-array function (see the Lisp Machine manual), and forms whose values are to be the arguments to those keywords. For example, (make-array (:type 'art-4b)) would request that the type of the array be art-4b. Note that the keyword symbols are \not/ evaluated. Constructor macros for structures implemented as arrays all allow the keyword make-array to be supplied. Its argument is of the same form as the make-array option, and attributes specified there (in the constructor form) will override those given in the defstruct form. Since it is sometimes necessary to be able to specify the dimensions of the array that defstruct is going to construct (for structures of type array-leader for example), the make-array option or constructor keyword accepts the additional keywords :length and :dimension (they mean the same thing). The argument to this pseudo make-array keyword will be supplied as the first argument to the make-array function when the constructor is expanded. defstruct chooses appropriate defaults for those attributes not specified in the defstruct form or in the constructor form, and defstruct overrides any specified attributes that it has to. .subsection "sfa-function" Available only on PDP-10s, this option allows the user to specify the function that will be used in structures of type sfa. Its argument should be a piece of code that evaluates to the desired function. Constructor macros for this type of structure will take sfa-function as a keyword whose argument is also the code to evaluate to get the function, overriding any supplied in the original defstruct form. If sfa-function is not present anywhere, then the constructor will use the name-symbol of the structure as the function. .subsection "sfa-name" Available only on PDP-10s, this option allows the user to specify the object that will be used in the printed representation of structures of type sfa. Its argument should be a piece of code that evaluates to that object. Constructor macros for this type of structure will take sfa-name as a keyword whose argument is also the code to evaluate to get the object to use, overriding any supplied in the original defstruct form. If sfa-name is not present anywhere, then the constructor will use the name-symbol of the structure as the function. .subsection "external-ptr" Available only on Multics, this option is used with structures of type external. Its argument should be a piece of code that evaluates to a fixnum "packed pointer" pointing to the first word of the external array the defstruct is to construct. Constructor macros for this type of structure will take external-ptr as a keyword whose argument overrides any supplied in the original defstruct form. If external-ptr is not present anywhere, then the constructor signals an error when it expands. .subsection "size-symbol" The size-symbol option allows a user to specify a symbol whose value will be the "size" of the structure. The exact meaning of this varies, but in general this number is the one you would need to know if you were going to allocate one of these structures yourself. The symbol will have this value both at compile time and at run time. If this option is present without an argument, then the name of the structure is concatenated with "-size" to produce the symbol. .subsection "size-macro" Similar to size-symbol. A macro of no arguments is defined that expands into the size of the structure. The name of this macro defaults as with size-symbol. .subsection "initial-offset" This option 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, which must be a fixnum, which is the number of slots you want defstruct to skip. To make use of this option requires that you have some familiarity with how defstruct is implementing you structure, otherwise you will be unable to make use of the slots that defstruct has left unused. .subsection "but-first" This option is best explained by example: (defstruct (head (type list) (default-pointer person) (but-first person-head)) nose mouth eyes) So now the accessors expand like this: (nose x) ==> (car (person-head x)) (nose) ==> (car (person-head person)) The theory is that but-first's argument will likely be an accessor from some other structure, and it is never expected that this structure will be found outside of that slot of that other structure. (In the example I had in mind that there was a person structure which had a slot accessed by person-head.) It is an error for the but-first option to be used without an argument. .subsection "callable-accessors" This option controls whether the accessors defined by defstruct will work as "functional arguments". (As the first argument to mapcar, for example.) On the Lisp Machine accessors are callable by default, but on PDP-10s it is expensive to make this work, so they are only callable if you ask for it. (Currently on Multics the feature doesn't work at all...) The argument to this option is nil to indicate that the feature should be turned off, and t to turn the feature on. If the option is present with no argument, then the feature is turned on. .subsection "eval-when" Normally the macros defined by defstruct are defined at eval-time, compile-time and at load-time. This option allows the user to control this behavior. (eval-when (eval compile)), for example, will cause the macros to be defined only when the code is running interpreted and inside the compiler, no trace of defstruct will be found when running compiled code. Using the eval-when option is preferable to wrapping an eval-when around a defstruct form, since nested eval-whens can interact in unexpected ways. .subsection "property" For each structure defined by defstruct, a property list is maintained for the recording of arbitrary properties about that structure. The property option can be used to give a defstruct an arbitrary property. (property ) gives the defstruct a property of . Neither argument is evaluated. To access the property list, the user will have to look inside the defstruct-description structure himself, he is referred to (defstruct-description-section-page), for more information. .subsection "A Type Used As An Option" In addition to the options listed above, any currently defined type (a legal argument to the type option) can be used as a option. This is mostly for compatibility with the old Lisp Machine defstruct. It allows you to say just when you should be saying (type ). Use of this feature in new code is discouraged. It is an error to give an argument to a type used as an option in this manner. .subsection "Other Options" Finally, if an option isn't found among those listed above, defstruct checks the property list of the name of the option to see if it has a non-null defstruct-option property. If is does have such a property, then if the option was of the form ( ), it is treated just like (property ). That is, the defstruct is given an property of . It is an error to use such an option without a value. This provides a primitive way for the user to define his own options to defstruct. Several of the options listed above are actually implemented using this mechanism. .section "Byte Fields" On Multics, the byte field feature will not work unless the user has arranged to define the functions ldb and dpb ((byte-hacking-section-page)). They are not yet present in the default environment, but they are available as part of the extension library ((multics-library-section-page)). The byte field feature of defstruct allows the user to specify that several slots of his structure are bytes in a fixed point number stored in one element of the structure. For example, suppose we had the following structure: (defstruct (phone-book-entry (type list)) name address (area-code 617.) exchange line-number) This will work just fine. Except you notice that an area-code and an exchange are both always less than 1000., and so both can easily fit in 10. bits, and the line-number is always less than 10000. and can thus fit in 14. bits. Thus you can pack all three parts of a phone number in 34. bits. If you have a lisp with 36. bit fixnums, then you should be able to put the entire phone number in one fixnum in a structure. defstruct allows you to do this as follows: (defstruct (phone-book-entry (type list)) name address ((area-code 3012 617.) (exchange 1612) (line-number 0016))) The magic numbers 3012, 1612 and 0016 are byte specifiers suitable for use with the functions ldb and dpb ((ldb-fun)). Things will expand as follows: (area-code pbe) ==> (ldb 3012 (caddr pbe)) (exchange pbe) ==> (ldb 1612 (caddr pbe)) (make-phone-book-entry name '|Fred Derf| address '|259 Octal St.| exchange ex line-number 7788.) ==> (list '|Fred Derf| '|259 Octal St.| (dpb ex 1612 115100017154)) (alter-phone-book-entry pbe exchange ex line-number ln) ==> ((lambda (G0003) (setf (caddr G0003) (dpb ex 1612 (dpb ln 0016 (caddr G0003))))) pbe) defstruct tries to be maximally clever about constructing and altering structures with byte fields. The byte specifiers are actually pieces of code that are expected to evaluate to byte specifiers, but defstruct will try and understand fixnums if you supply them. (In the make-phone-book example, defstruct was able to make use of its knowledge of the line-number and area-code byte specifiers to assemble the constant number 115100017154 and produce code to just deposit in the exchange.) A nil in the place of the byte specifier code means to define an accessor for the entire word. So we could say: (defstruct (phone-book-entry (type list)) name address ((phone-number nil) (area-code 3012 617.) (exchange 1612) (line-number 0016))) to enable us to do things like: (setf (phone-number pbe1) (phone-number pbe2)) to cause two entries to have the same phone numbers. We could also have said just: ((phone-number) ...) in that last defstruct, but the feature of nil byte specifiers allows you to supply initial values for the entire slot by saying: (( nil ) ...). Constructor macros initialize words divided into byte fields as if they were deposited in the following order: 1) Initializations for the entire word given in the defstruct form. 2) Initializations for the byte fields given in the defstruct form. 3) Initializations for the entire word given in the constructor macro form. 4) Initializations for the byte fields given in the constructor macro form. Alterant macros operate in a similar manner. That is, as if the entire word was modified first, and then the byte fields were deposited. Results will be unpredictable in constructing and altering if byte fields that overlap are given. .section "About Autoloading" This section only applies to PDP-10 and Multics Lisp. If you look at the property lists of the macros defined by defstruct, you will find that they are all have macro properties of one of four functions: defstruct-expand-ref-macro, defstruct-expand-cons-macro, defstruct-expand-alter-macro and defstruct-expand-size-macro. These functions figure out how to expand the macro by examining the property list of the car of the form they are asked to expand. defstruct-expand-ref-macro, for example, looks for a defstruct-slot property, which should be a cons of the form ( . ). Since the defstruct form only expands into putprops of the desired functions (instead of actually constructing a full-fledged definition), loading a compiled file containing a defstruct merely adds a few properties to some symbols. The run time environment is not needlessly cluttered with unwanted list structure or subr objects. If the user thinks he may wish to use any of the macros defined by defstruct after compiling his file, he need only give the four expanding functions autoload properties of the name of the file containing defstruct itself. For purposes of using defstruct interpreted, the two symbols defstruct and defstruct-define-type should be given similar autoload properties. Thus six symbols with autoload properties suffice to make defstruct appear loaded at all times. .section "The defstruct-description Structure" This section discusses the internal structures used by defstruct that might be useful to programs that want to interface to defstruct nicely. The information in this section is also necessary for anyone who is thinking of defining his own structure types ((extension-section-page)). Lisp Machine programmers will find that the symbols found only in this section are all interned in the "systems-internals" package. Whenever the user defines a new structure using defstruct, defstruct creates an instance of the defstruct-description structure. This structure can be found as the defstruct-description property of the name of the structure; it contains such useful information as the name of the structure, the number of slots in the structure, etc. The defstruct-description structure is defined something like this: (This is a bowdlerized version of the real thing, I have left out a lot of things you don't need to know unless you are actually reading the code.) (defstruct (defstruct-description (default-pointer description) (conc-name defstruct-description-)) name size property-alist slot-alist) The name slot contains the symbol supplied by the user to be the name of his structure, something like spaceship or phone-book-entry. The size slot contains the total number of slots in an instance of this kind of structure. This is \not/ the same number as that obtained from the size-symbol or size-macro options to defstruct. A named structure, for example, usually uses up an extra location to store the name of the structure, so the size-macro option will get a number one larger than that stored in the defstruct description. The property-alist slot contains an alist with pairs of the form ( . ) containing properties placed there by the property option to defstruct or by property names used as options to defstruct (see (property-section-page), and (other-options-section-page)). The slot-alist slot contains an alist of pairs of the form ( . ). A is an instance of the defstruct-slot-description structure. The defstruct-slot-description structure is defined something like this: (another bowdlerized defstruct) (defstruct (defstruct-slot-description (default-pointer slot-description) (conc-name defstruct-slot-description-)) number ppss init-code ref-macro-name) The number slot contains the number of the location of this slot in an instance of the structure. Locations are numbered starting with 0, and continuing up to one less than the size of the structure. The actual location of the slot is determined by the reference consing code associated with the type of the structure. See (extension-section-page). The ppss slot contains the byte specifier code for this slot if this slot is a byte field of its location. If this slot is the entire location, then the ppss slot contains nil. The init-code slot contains the initialization code supplied for this slot by the user in his defstruct form. If there is no initialization code for this slot then the init-code slot contains the symbol %%defstruct-empty%%. The ref-macro-name slot contains the symbol that is defined as a macro that expands into a reference to this slot. .section "Extensions to defstruct" .defmac defstruct-define-type The macro defstruct-define-type can be used to teach defstruct about new types it can use to implement structures. .subsection "A Simple Example" Let us start by examining a sample call to defstruct-define-type. This is how the list type of structure might have been defined: (defstruct-define-type list (cons (initialization-list description keyword-options) list (cons 'list initialization-list)) (ref (slot-number description argument) (list 'nth slot-number argument))) This is the minimal example. We have provided defstruct with two pieces of code, one for consing up forms to construct instances of the structure, the other to cons up forms to reference various elements of the structure. From the example we can see that the constructor consing code is going to be run in an environment where the variable initialization-list is bound to a list which is the initializations to the slots of the structure arranged in order. The variable description will be bound to the defstruct-description structure for the structure we are consing a constructor for. (See (defstruct-description-section-page).) The binding of the variable keyword-options will be described later. Also the symbol list appears after the argument list, this conveys some information to defstruct about how the constructor consing code wants to get called. The reference consing code gets run with the variable slot-number bound to the number of the slot that is to be referenced and the variable argument bound to the code that appeared as the argument to the accessor macro. The variable description is again bound to the appropriate instance of the defstruct-description structure. This simple example probably tells you enough to be able to go ahead and implement other structure types, but more details follow. .subsection "Syntax of defstruct-define-type" The syntax of defstruct-define-type is (defstruct-define-type ... ) where each is either the symbolic name of an option or a list of the form ( . ). (Actually is the same as ().) Different options interpret in different ways. The symbol is given a defstruct-type-description property of a structure that describes the type completely. .subsection "Options to defstruct-define-type" This section is a catalog of all the options currently known about by defstruct-define-type. .subsubsection "cons" The cons option to defstruct-define-type is how the user supplies defstruct with the necessary code that it needs to cons up a form that will construct an instance of a structure of this type. The cons option has the syntax: (cons ( ) ) is some code that should construct and return a piece of code that will construct, initialize and return an instance of a structure of this type. The symbol will be bound to the code that the constructor conser should use to initialize the slots of the structure. The exact form of this argument is determined by the symbol . There are currently two kinds of initialization. There is the list kind, where is bound to a list of initializations, in the correct order, with nils in uninitialized slots. And there is the alist kind, where is bound to an alist with pairs of the form ( . ). The symbol will be bound to the instance of the defstruct-description structure ((defstruct-description-section-page)) that defstruct maintains for this particular structure. This is so that the constructor conser can find out such things as the total size of the structure it is supposed to create. The symbol will be bound to a alist with pairs of the form ( . ), where each was a keyword supplied to the constructor macro that wasn't the name of a slot, and was the "code" that followed the keyword. (See (keywords-section-page), and (constructor-section-page).) It is an error not to supply the cons option to defstruct-define-type. .subsubsection "ref" The ref option to defstruct-define-type is how the user supplies defstruct with the necessary code that it needs to cons up a form that will reference an instance of a structure of this type. The ref option has the syntax: (ref ( ... ) ) is some code that should construct and return a piece of code that will reference an instance of a structure of this type. The symbol will be bound to the location of the slot that the is to be referenced. This is the same number that is found in the number slot of the defstruct-slot-description structure ((defstruct-description-section-page)). The symbol will be bound to the instance of the defstruct-description structure that defstruct maintains for this particular structure. The symbols are bound to the forms supplied to the accessor as arguments. Normally there should be only one of these. The \last/ argument is the one that will be defaulted by the default-pointer option ((default-pointer-section-page)). defstruct will check that the user has supplied exactly n arguments to the accessor macro before calling the reference consing code. It is an error not to supply the ref option to defstruct-define-type. .subsubsection "overhead" The overhead option to defstruct-define-type is how the user declares to defstruct that the implementation of this particular type of structure "uses up" some number of slots locations in the object actually constructed. This option is used by various "named" types of structures that store the name of the structure in one location. The syntax of overhead is: (overhead ) where is a fixnum that says how many locations of overhead this type needs. This number is only used by the size-macro and size-symbol options to defstruct. (See (size-macro-section-page), and (size-symbol-section-page).) .subsubsection "named" The named option to defstruct-define-type controls the use of the named option to defstruct. With no argument the named option means that this type is an acceptable "named structure". With an argument, as in (named ), the symbol should be that name of some other structure type that defstruct should use if someone asks for the named version of this type. (For example, in the definition of the list type the named option is used like this: (named named-list).) .subsubsection "keywords" The keywords option to defstruct-define-type allows the user to define constructor keywords ((constructor-section-page)) for this type of structure. (For example the make-array constructor keyword for structures of type array on Lisp Machines.) The syntax is: (keywords ... ) where each is a symbol that the constructor conser expects to find in the alist ((cons-option-section-page)). .subsubsection "defstruct" The defstruct option to defstruct-define-type allows the user to run some code and return some forms as part of the expansion of the defstruct macro. The defstruct option has the syntax: (defstruct () ) is a piece of code that will be run whenever defstruct is expanding a defstruct form that defines a structure of this type. The symbol will be bound to the instance of the defstruct-description structure that defstruct maintains for this particular structure. The value returned by the defstruct option should be a \list/ of forms to be included with those that the defstruct expands into. Thus, if you only want to run some code at defstruct expand time, and you don't want to actually output any additional code, then you should be careful to return nil from the code in this option.