### 2.10 Structure Definitions

This section provides examples and describes the options and syntax of `define-structure`, an MIT/GNU Scheme macro that is very similar to `defstruct` in Common Lisp. The differences between them are summarized at the end of this section. For more information, see Steele’s Common Lisp book.

special form: define-structure (name structure-option …) slot-description …

  Each slot-description takes one of the following forms:

      slot-name
      (slot-name default-init [slot-option value]*)

  The fields name and slot-name must both be symbols. The field default-init is an expression for the initial value of the slot. It is evaluated each time a new instance is constructed. If it is not specified, the initial content of the slot is undefined. Default values are only useful with a BOA constructor with argument list or a keyword constructor (see below).

  Evaluation of a `define-structure` expression defines a structure descriptor and a set of procedures to manipulate instances of the structure. These instances are represented as records by default (see Records) but may alternately be lists or vectors. The accessors and modifiers are marked with compiler declarations so that calls to them are automatically transformed into appropriate references. Often, no options are required, so a simple call to `define-structure` looks like:

      (define-structure foo a b c)

  This defines a type descriptor `rtd:foo`, a constructor `make-foo`, a predicate `foo?`, accessors `foo-a`, `foo-b`, and `foo-c`, and modifiers `set-foo-a!`, `set-foo-b!`, and `set-foo-c!`.

  In general, if no options are specified, `define-structure` defines the following (using the simple call above as an example):

  type descriptor

    The name of the type descriptor is `"rtd:"` followed by the name of the structure, e.g. ‘`rtd:foo`’. The type descriptor satisfies the predicate `record-type?`.

  constructor

    The name of the constructor is `"make-"` followed by the name of the structure, e.g. ‘`make-foo`’. The number of arguments accepted by the constructor is the same as the number of slots; the arguments are the initial values for the slots, and the order of the arguments matches the order of the slot definitions.

  predicate

    The name of the predicate is the name of the structure followed by `"?"`, e.g. ‘`foo?`’. The predicate is a procedure of one argument, which returns `#t` if its argument is a record of the type defined by this structure definition, and `#f` otherwise.

  accessors

    For each slot, an accessor is defined. The name of the accessor is formed by appending the name of the structure, a hyphen, and the name of the slot, e.g. ‘`foo-a`’. The accessor is a procedure of one argument, which must be a record of the type defined by this structure definition. The accessor extracts the contents of the corresponding slot in that record and returns it.

  modifiers

    For each slot, a modifier is defined. The name of the modifier is formed by appending `"set-"`, the name of the accessor, and `"!"`, e.g. ‘`set-foo-a!`’. The modifier is a procedure of two arguments, the first of which must be a record of the type defined by this structure definition, and the second of which may be any object. The modifier modifies the contents of the corresponding slot in that record to be that object, and returns an unspecified value.

  When options are not supplied, `(name)` may be abbreviated to name. This convention holds equally for structure-options and slot-options. Hence, these are equivalent:

      (define-structure foo a b c)
      (define-structure (foo) (a) b (c))

  as are

      (define-structure (foo keyword-constructor) a b c)
      (define-structure (foo (keyword-constructor)) a b c)

  When specified as option values, `false` and `nil` are equivalent to `#f`, and `true` and `t` are equivalent to `#t`.

Possible slot-options are:

slot option: read-only value

  When given a value other than `#f`, this specifies that no modifier should be created for the slot.

slot option: type type-descriptor

  This is accepted but not presently used.

Possible structure-options are:

structure option: predicate [name]

  This option controls the definition of a predicate procedure for the structure. If name is not given, the predicate is defined with the default name (see above). If name is `#f`, the predicate is not defined at all. Otherwise, name must be a symbol, and the predicate is defined with that symbol as its name.

structure option: copier [name]

  This option controls the definition of a procedure to copy instances of the structure. This is a procedure of one argument, a structure instance, that makes a newly allocated copy of the structure and returns it. If name is not given, the copier is defined, and the name of the copier is `"copy-"` followed by the structure name (e.g. ‘`copy-foo`’). If name is `#f`, the copier is not defined. Otherwise, name must be a symbol, and the copier is defined with that symbol as its name.

structure option: print-procedure expression

  Evaluating expression must yield a procedure of two arguments, which is used to print instances of the structure. The procedure is an unparser method (see Custom Output). If the structure instances are records, this option has the same effect as calling `set-record-type-unparser-method!`.

structure option: constructor [name [argument-list]]

  This option controls the definition of constructor procedures. These constructor procedures are called “BOA constructors”, for “By Order of Arguments”, because the arguments to the constructor specify the initial contents of the structure’s slots by the order in which they are given. This is as opposed to “keyword constructors”, which specify the initial contents using keywords, and in which the order of arguments is irrelevant.

  If name is not given, a constructor is defined with the default name and arguments (see above). If name is `#f`, no constructor is defined; argument-list may not be specified in this case. Otherwise, name must be a symbol, and a constructor is defined with that symbol as its name. If name is a symbol, argument-list is optionally allowed; if it is omitted, the constructor accepts one argument for each slot in the structure definition, in the same order in which the slots appear in the definition. Otherwise, argument-list must be a lambda list (see Lambda Expressions), and each of the parameters of the lambda list must be the name of a slot in the structure. The arguments accepted by the constructor are defined by this lambda list. Any slot that is not specified by the lambda list is initialized to the default-init as specified above; likewise for any slot specified as an optional parameter when the corresponding argument is not supplied.

  If the `constructor` option is specified, the default constructor is not defined. Additionally, the `constructor` option may be specified multiple times to define multiple constructors with different names and argument lists.

      (define-structure (foo
                         (constructor make-foo (#!optional a b)))
        (a 6 read-only #t)
        (b 9))

structure option: keyword-constructor [name]

  This option controls the definition of keyword constructor procedures. A keyword constructor is a procedure that accepts arguments that are alternating slot names and values. If name is omitted, a keyword constructor is defined, and the name of the constructor is `"make-"` followed by the name of the structure (e.g. ‘`make-foo`’). Otherwise, name must be a symbol, and a keyword constructor is defined with this symbol as its name.

  If the `keyword-constructor` option is specified, the default constructor is not defined. Additionally, the `keyword-constructor` option may be specified multiple times to define multiple keyword constructors; this is usually not done since such constructors would all be equivalent.

      (define-structure (foo (keyword-constructor make-bar)) a b)
      (foo-a (make-bar 'b 20 'a 19))         ⇒ 19

structure option: type-descriptor name

  This option cannot be used with the `type` or `named` options.

  By default, structures are implemented as records. The name of the structure is defined to hold the type descriptor of the record defined by the structure. The `type-descriptor` option specifies a different name to hold the type descriptor.

      (define-structure foo a b)
      foo             ⇒ #[record-type 18]

      (define-structure (bar (type-descriptor <bar>)) a b)
      bar             error→ Unbound variable: bar
      <bar>         ⇒ #[record-type 19]

structure option: conc-name [name]

  By default, the prefix for naming accessors and modifiers is the name of the structure followed by a hyphen. The `conc-name` option can be used to specify an alternative. If name is not given, the prefix is the name of the structure followed by a hyphen (the default). If name is `#f`, the slot names are used directly, without prefix. Otherwise, name must a symbol, and that symbol is used as the prefix.

      (define-structure (foo (conc-name moby/)) a b)

  defines accessors `moby/a` and `moby/b`, and modifiers `set-moby/a!` and `set-moby/b!`.

      (define-structure (foo (conc-name #f)) a b)

  defines accessors `a` and `b`, and modifiers `set-a!` and `set-b!`.

structure option: type representation-type

  This option cannot be used with the `type-descriptor` option.

  By default, structures are implemented as records. The `type` option overrides this default, allowing the programmer to specify that the structure be implemented using another data type. The option value representation-type specifies the alternate data type; it is allowed to be one of the symbols `vector` or `list`, and the data type used is the one corresponding to the symbol.

  If this option is given, and the `named` option is not specified, the representation will not be tagged, and neither a predicate nor a type descriptor will be defined; also, the `print-procedure` option may not be given.

      (define-structure (foo (type list)) a b)
      (make-foo 1 2)                          ⇒ (1 2)

structure option: named [expression]

  This is valid only in conjunction with the `type` option and specifies that the structure instances be tagged to make them identifiable as instances of this structure type. This option cannot be used with the `type-descriptor` option.

  In the usual case, where expression is not given, the `named` option causes a type descriptor and predicate to be defined for the structure (recall that the `type` option without `named` suppresses their definition), and also defines a default unparser method for the structure instances (which can be overridden by the `print-procedure` option). If the default unparser method is not wanted then the `print-procedure` option should be specified as `#F`. This causes the structure to be printed in its native representation, as a list or vector, which includes the type descriptor. The type descriptor is a unique object, not a record type, that describes the structure instances and is additionally stored in the structure instances to identify them: if the representation type is `vector`, the type descriptor is stored in the zero-th slot of the vector, and if the representation type is `list`, it is stored as the first element of the list.

      (define-structure (foo (type vector) named) a b c)
      (vector-ref (make-foo 1 2 3) 0) ⇒ #[structure-type 52]

  If expression is specified, it is an expression that is evaluated to yield a tag object. The expression is evaluated once when the structure definition is evaluated (to specify the unparser method), and again whenever a predicate or constructor is called. Because of this, expression is normally a variable reference or a constant. The value yielded by expression may be any object at all. That object is stored in the structure instances in the same place that the type descriptor is normally stored, as described above. If expression is specified, no type descriptor is defined, only a predicate.

      (define-structure (foo (type vector) (named 'foo)) a b c)
      (vector-ref (make-foo 1 2 3) 0) ⇒ foo

structure option: safe-accessors [boolean]

  This option allows the programmer to have some control over the safety of the slot accessors (and modifiers) generated by `define-structure`. If `safe-accessors` is not specified, or if boolean is `#f`, then the accessors are optimized for speed at the expense of safety; when compiled, the accessors will turn into very fast inline sequences, usually one to three machine instructions in length. However, if `safe-accessors` is specified and boolean is either omitted or `#t`, then the accessors are optimized for safety, will check the type and structure of their argument, and will be close-coded.

      (define-structure (foo safe-accessors) a b c)

structure option: initial-offset offset

  This is valid only in conjunction with the `type` option. Offset must be an exact non-negative integer and specifies the number of slots to leave open at the beginning of the structure instance before the specified slots are allocated. Specifying an offset of zero is equivalent to omitting the `initial-offset` option.

  If the `named` option is specified, the structure tag appears in the first slot, followed by the “offset” slots, and then the regular slots. Otherwise, the “offset” slots come first, followed by the regular slots.

      (define-structure (foo (type vector) (initial-offset 3))
        a b c)
      (make-foo 1 2 3)                ⇒ #(() () () 1 2 3)

The essential differences between MIT/GNU Scheme’s `define-structure` and Common Lisp’s `defstruct` are:

- The default constructor procedure takes positional arguments, in the same order as specified in the definition of the structure. A keyword constructor may be specified by giving the option `keyword-constructor`.
- BOA constructors are described using Scheme lambda lists. Since there is nothing corresponding to `&aux` in Scheme lambda lists, this functionality is not implemented.
- By default, no `copier` procedure is defined.
- The side-effect procedure corresponding to the accessor `foo` is given the name `set-foo!`.
- Keywords are ordinary symbols – use `foo` instead of `:foo`.
- The option values `false`, `nil`, `true`, and `t` are treated as if the appropriate boolean constant had been specified instead.
- The `print-function` option is named `print-procedure`. Its argument is a procedure of two arguments (the unparser state and the structure instance) rather than three as in Common Lisp.
- By default, named structures are tagged with a unique object of some kind. In Common Lisp, the structures are tagged with symbols. This depends on the Common Lisp package system to help generate unique tags; MIT/GNU Scheme has no such way to generate unique symbols.
- The `named` option may optionally take an argument, which is normally the name of a variable (any expression may be used, but it is evaluated whenever the tag name is needed). If used, structure instances will be tagged with that variable’s value. The variable must be defined when `define-structure` is evaluated.
- The `type` option is restricted to the values `vector` and `list`.
- The `include` option is not implemented.