Next: Macros, Previous: Iteration, Up: Special Forms
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.
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 todefine-structure
looks like:(define-structure foo a b c)This defines a type descriptor
rtd:foo
, a constructormake-foo
, a predicatefoo?
, accessorsfoo-a
,foo-b
, andfoo-c
, and modifiersset-foo-a!
,set-foo-b!
, andset-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 predicaterecord-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
andnil
are equivalent to#f
, andtrue
andt
are equivalent to#t
.
Possible slot-options are:
When given a value other than
#f
, this specifies that no modifier should be created for the slot.
Possible structure-options are:
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.
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.
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!
.
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, theconstructor
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))
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, thekeyword-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
This option cannot be used with the
type
ornamed
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]
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
andmoby/b
, and modifiersset-moby/a!
andset-moby/b!
.(define-structure (foo (conc-name #f)) a b)
defines accessors
a
andb
, and modifiersset-a!
andset-b!
.
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 symbolsvector
orlist
, 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, theprint-procedure
option may not be given.(define-structure (foo (type list)) a b) (make-foo 1 2) => (1 2)
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 thetype-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 thetype
option withoutnamed
suppresses their definition), and also defines a default unparser method for the structure instances (which can be overridden by theprint-procedure
option). If the default unparser method is not wanted then theprint-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 isvector
, the type descriptor is stored in the zero-th slot of the vector, and if the representation type islist
, 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
This option allows the programmer to have some control over the safety of the slot accessors (and modifiers) generated by
define-structure
. Ifsafe-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, ifsafe-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)
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 theinitial-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:
keyword-constructor
.
&aux
in Scheme lambda lists, this
functionality is not implemented.
copier
procedure is defined.
foo
is
given the name set-foo!
.
foo
instead of :foo
.
false
, nil
, true
, and t
are treated as if the appropriate boolean constant had been specified
instead.
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.
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.
type
option is restricted to the values vector
and
list
.
include
option is not implemented.