Next: Macros, Previous: Iteration, Up: Special Forms [Contents][Index]
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 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):
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?
.
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.
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.
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.
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:
When given a value other than #f
, this specifies that no
modifier should be created for the slot.
This is accepted but not presently used.
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 a print method (see Custom Output).
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))
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
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]
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!
.
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)
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 print method
for the structure instances (which can be overridden by the
print-procedure
option). If the default print 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 print 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
. 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)
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:
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 structure instance and a
textual output port) 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.
Next: Macros, Previous: Iteration, Up: Special Forms [Contents][Index]