Structures
A structure type is a record datatype composing a number
of fields. A structure, an instance of a
structure type, is a first-class value that contains a value for each
field of the structure type. A structure instance is created with a
type-specific constructor procedure, and its field values are
accessed and changed with type-specific selector and setter
procedures. In addition, each structure type has a predicate
procedure that answers #t
for instances of the structure type
and #f
for any other value.
4.1 Defining Structure Types
A new structure type can be created with one of four
define-struct
forms:
(define-struct s (field
···) [inspector-expr]) (define-struct (s t) (field
···) [inspector-expr])
where s
, t
, and each field
are identifiers. The
latter form is described in section 4.2. The optional
inspector-expr
, which should produce an inspector or
#f
, is explained in section 4.5.
A define-struct
expression with n
field
s defines
4 + 2n names:
struct:
s
, a structure type descriptor value that represents the new datatype. This value is rarely used directly.make-
s
, a constructor procedure that takesn
arguments and returns a new structure value.s
?
, a predicate procedure that returns#t
for a value constructed bymake-
s
(or the constructor for a subtype; see section 4.2) and#f
for any other value.s
-
field
, for eachfield
, an accessor procedure that takes a structure value and extracts the value forfield
.set-
s
-
field
!
, for eachfield
, a mutator procedure that takes a structure and a new field value. The field value in the structure is destructively updated with the new value, and void is returned.s
, a syntax binding that encapsulates information about the structure type declaration. This binding is used to define subtypes (see section 4.2). It also works with theshared
andmatch
forms (see Chapter 44 and Chapter 27 in PLT MzLib: Libraries Manual). For detailed information about the expansion-time information stored ins
, see section 12.6.4.
Each time a define-struct
expression is evaluated, a new
structure type is created with distinct constructor, predicate,
accessor, and mutator procedures. If the same define-struct
expression is evaluated twice, instances created by the constructor
returned by the first evaluation will answer #f
to the
predicate returned by the second evaluation.
Examples:
(define-struct cons-cell (car
cdr
)) (define x (make-cons-cell 1 2)) (cons-cell? x) ; =>#t
(cons-cell-car x) ; =>1
(set-cons-cell-car! x 5) (cons-cell-car x) ; =>5
(define orig-cons-cell? cons-cell?) (define-struct cons-cell (car
cdr
)) (define y (make-cons-cell 1 2)) (cons-cell? y) ; =>#t
(cons-cell? x) ; =>#f
,cons-cell?
now checks for a different type (orig-cons-cell? x) ; =>#t
(orig-cons-cell? y) ; =>#f
The let-struct
form binds structure identifiers in a
lexical scope; it does not support an inspector-expr
.
(let-struct s (field ···) body-expr ···1) (let-struct (s t) (field ···) body-expr ···1)
4.2 Creating Subtypes
The latter define-struct
form shown in
section 4.1 creates a new structure type that is a
structure subtype of an existing base structure type. An
instance of a structure subtype can always be used as an instance of
the base structure type, but the subtype gets its own predicate
procedure and may have its own fields in addition to the fields of
the base type.
The t
identifier in a subtyping define-struct
form must
be bound to syntax describing a structure type declaration. Normally,
it is the name of a structure type previously declared with
define-struct
. The information associated with t
is
used to access the base structure type for the new subtype.
A structure subtype ``inherits'' the fields of its base type. If the
base type has m
fields, and if n
fields are specified in
the subtyping define-struct
expression, then the resulting
structure type has m + n fields. Consequently, m + n field values
must be provided to the subtype's constructor procedure. Values for
the first m
fields of a subtype instance are accessed with
selector procedures for the original base type, and the last n
are accessed with subtype-specific selectors. Subtype-specific
accessors and mutators for the first m
fields are not created.
Examples:
(define-struct cons-cell (car
cdr
)) (define x (make-cons-cell 1 2)) (define-struct (tagged-cons-cell cons-cell) (tag)) (define z (make-tagged-cons-cell 3 4 't)) (cons-cell? z) ; =>#t
(tagged-cons-cell? z) ; =>#t
(tagged-cons-cell? x) ; =>#f
(cons-cell-car z) ; =>3
(tagged-cons-cell-tag z) ; =>'t
4.3 Structure Types with Automatic Fields, Immutable Fields, and Properties
The
procedure creates a new structure type in
the same way as the make-struct-type
define-struct
form of
section 4.1, but provides a more general interface. In
particular, the
procedure supports structure
type properties.make-struct-type
(make-struct-type
name-symbol super-struct-type init-field-k auto-field-k
[auto-v prop-value-list inspector-or-false proc-spec immutable-k-list guard-proc
])
creates a new structure type. Thename-symbol
argument is used as the type name. Ifsuper-struct-type
is not#f
, the new type is a subtype of the corresponding structure type, as described in section 4.2.The new structure type has
init-field-k
+auto-field-k
fields (in addition to any fields fromsuper-struct-type
), but onlyinit-field-k
constructor arguments (in addition to any constructor arguments fromsuper-struct-type
). The remaining fields are initialized withauto-v
, which defaults to#f
.The
prop-value-list
argument is a list of pairs, where the
of each pair is a structure type property descriptor, and thecar
is an arbitrary value. The default iscdr
. See section 4.4 for more information about properties.null
The
inspector-or-false
argument controls access to debugging information about the structure type and its instances; see section 4.5 for more information.The
proc-spec
argument can be#f
, an exact non-negative integer, or a procedure. The default is#f
. If an integer or procedure is provided, instances of the structure type act as procedures. See section 4.6 for further information. Providing a non-#f
value forproc-spec
is the same as pairing the value withprop:procedure
inprop-value-list
, plus includingproc-spec
inimmutable-k-list
whenproc-spec
is an integer.The
immutable-k-list
argument provides a list of exact, non-negative integers that identify immutable field positions. Each element in the list should be unique, otherwiseexn:fail:contract
exception is raised. Each element should also fall in the range0
(inclusive) andinit-field-k
(exclusive), otherwiseexn:fail:contract
exception is raised.The
guard-proc
argument is either a procedure ofn
+ 1 arguments or#f
, wheren
is the number of arguments for the new structure type's constructor (i.e.,init-field-k
plus constructor arguments implied bysuper-struct-type
, if any). Ifguard-proc
is a procedure, then the procedure is called whenever an instance of the type is constructed, or whenever an instance of a subtype is created. The arguments toguard-proc
are the values provided for the structure's firstn
fields, followed by the name of the instantiated structure type (which isname-symbol
, unless a subtype is instantiated). Theguard-proc
result should ben
values, which become the actual value for the structure's fields. Theguard-proc
can raise an exception to prevent creation of a structure with the given field values. If a structure subtype has its own guard, the subtype guard is applied first, and the firstn
values produced by the subtype's guard procedure become the firstn
arguments toguard-proc
.The result of
make-struct-type
is five values, which are similar to the values produced bydefine-struct
(see section 4.1):a structure type descriptor,
a constructor procedure,
a predicate procedure,
an accessor procedure, which consumes a structure and a field index between 0 (inclusive) and
init-field-k
+auto-field-k
(exclusive), anda mutator procedure, which consumes a structure, a field index, and a field value.
Unlike define-struct
,
returns a single
accessor procedure and a single mutator procedure for all fields.
The make-struct-type
and
make-struct-field-accessor
procedures convert a type-specific
accessor or mutator returned by make-struct-field-mutator
into a
field-specific accessor or mutator:
make-struct-type
(make-struct-field-accessor
accessor-proc field-pos-k field-name-symbol
)
returns a field accessor that is equivalent toThe(lambda (s) (accessor-proc s field-pos-k))
accessor-proc
must be an accessor returned by
. The name of the resulting procedure for debugging purposes is derived frommake-struct-type
field-name-symbol
and the name ofaccessor-proc
's structure type.(make-struct-field-mutator
mutator-proc field-pos-k field-name-symbol
)
returns a field mutator that is equivalent toThe(lambda (s v) (mutator-proc s field-pos-k v))
mutator-proc
must be a mutator returned by
. The name of the resulting procedure for debugging purposes is derived frommake-struct-type
field-name-symbol
and the name ofmutator-proc
's structure type.
Examples:
(define-values (struct:a make-a a? a-ref a-set!) (make-struct-type
'a #f 2 1 'uninitialized)) (define an-a (make-a 'x 'y)) (a-ref an-a 1) ; =>'y
(a-ref an-a 2) ; =>'uninitialized
(define a-first (make-struct-field-accessor
a-ref 0)) (a-first an-a) ; =>'x
(define-values (struct:b make-b b? b-ref b-set!) (make-struct-type
'b struct:a 1 2 'b-uninitialized)) (define a-b (make-b 'x 'y 'z)) (a-ref a-b 1) ; =>'y
(a-ref a-b 2) ; =>'uninitialized
(b-ref a-b 0) ; =>z
(b-ref a-b 1) ; =>'b-uninitialized
(b-ref a-b 2) ; =>'b-uninitialized
(define-values (struct:c make-c c? c-ref c-set!) (make-struct-type
'c struct:b 0 0 #fnull
(make-inspector
) #fnull
;; Guard checks for a number, and makes it inexact (lambda (a1 a2 b1 name) (unless (number?
a2) (error
(string->symbol
(format
"make-~a" name)) "second field must be a number")) (values
a1 (exact->inexact
a2) b1)))) (make-c 'x 'y 'z) ; => error:"make-c: second field must be a number"
(define a-c (make-c 'x 2 'z)) (a-ref a-c 1) ; =>2.0
4.4 Structure Type Properties
A structure type property
allows per-type information to be associated with a structure type
(as opposed to per-instance information associated with a structure
value). A property value is associated with a structure type through
the
procedure (see
section 4.3). Subtypes inherit the property values
of their parent types, and subtypes can override an inherited
property value with a new value. (See section 11.2.10 for a
built-in property that controls how struct values are printed.)make-struct-type
(make-struct-type-property
name-symbol
[guard-proc
])
creates a new structure type property and returns three values:
a structure property type descriptor, for use with
;make-struct-type
a predicate procedure, which takes an arbitrary value and returns
#t
if the value is a descriptor or instance of a structure type that has a value for the property,#f
otherwise;an accessor procedure, which returns the value associated with structure type given its descriptor or one of its instances; if the structure type does not have a value for the property, or if any other kind of value is provided, the
exn:fail:contract
exception is raised.
If the optional guard-proc
is supplied, it is called by
before attaching the property to a new
structure type. The make-struct-type
guard-proc
must accept two arguments: a
value for the property supplied to
, and a
list containing information about the new structure type. The list
contains the values that make-struct-type
struct-type-info
would return for
the new structure type if it skipped the immediate current-inspector
control check (but not the check for exposing an ancestor structure
type, if any; see section 4.5).
The result of calling guard-proc
is associated with the
property in the target structure type, instead of the value supplied
to
. To reject a property association (e.g.,
because the value supplied to make-struct-type
is
inappropriate for the property), the guard can raise an
exception. Such an exception prevents make-struct-type
from
returning a structure type descriptor.make-struct-type
(struct-type-property?
v
)
returns #t
if v
is a
structure type property descriptor value, #f
otherwise.
Examples:
(define-values (prop:p p? p-ref) (make-struct-type-property
'p)) (define-values (struct:a make-a a? a-ref a-set!) (make-struct-type
'a #f 2 1 'uninitialized (list
(cons
prop:p 8)))) (p? struct:a) ; =>#t
(p? 13) ; =>#f
(define an-a (make-a 'x 'y)) (p? an-a) ; =>#t
(p-ref an-a) ; =>8
(define-values (struct:b make-b b? b-ref b-set!) (make-struct-type
'b #f 0 0 #f)) (p? struct:b) ; =>#f
4.5 Structure Inspectors
An inspector provides access to structure fields and structure type information without the normal field accessors and mutators. (Inspectors are also used to control access to module bindings; see section 9.4.) Inspectors are primarily intended for use by debuggers.
When a structure type is created, an inspector can be supplied. The given inspector is not the one that will control the new structure type; instead, the given inspector's parent will control the type. By using the parent of the given inspector, the structure type remains opaque to ``peer'' code that cannot access the parent inspector. Thus, an expression of the form
(define-struct s (field ···))
creates a structure type whose instances are opaque to peer code. In contrast, the following idiom creates a structure type that is transparent to peer code, because the supplied inspector is a newly created child of the current inspector:
(define-struct s (field ···) (make-inspector
))
Instead of supplying an inspector, #f
can be provided, which
makes the structure transparent to all code. Thus,
(define-struct s (field ···) #f)
creates a structure type that is transparent to all code.
The
parameter determines a default inspector
argument for new structure types. An alternate inspector can be
provided though the optional current-inspector
inspector-expr
expression of the
define-struct
form (see section 4.1), as shown
above, or through an optional inspector
argument to
(see section 4.3).make-struct-type
(make-inspector
[inspector
])
returns a new inspector that is a
subinspector of inspector
. If inspector
is not provided,
the new inspector is a subinspector of the current inspector. Any
structure type controlled by the new inspector is also controlled by
its ancestor inspectors, but no other inspectors.
(inspector?
v
)
returns #t
if v
is an inspector,
#f
otherwise.
The
and struct-info
procedures provide
inspector-based access to structure and structure type information:
struct-type-info
(struct-info
v
)
returns two values:struct-type
: a structure type descriptor or#f
; the result is a structure type descriptor of the most specific type for whichv
is an instance, and for which the current inspector has control, or the result is#f
if the current inspector does not control any structure type for which thestruct
is an instance.skipped?
:#f
if the first result corresponds to the most specific structure type ofv
,#t
otherwise.
(struct-type-info
struct-type
)
returns eight values that provide information about the structure type descriptorstruct-type
, assuming that the type is controlled by the current inspector:name-symbol
: the structure type's name as a symbol;init-field-k
: the number of fields defined by the structure type provided to the constructor procedure (not counting fields created by its ancestor types);auto-field-k
: the number of fields defined by the structure type without a counterpart in the constructor procedure (not counting fields created by its ancestor types);accessor-proc
: an accessor procedure for the structure type, like the one returned by
;make-struct-type
mutator-proc
: a mutator procedure for the structure type, like the one returned by
;make-struct-type
immutable-k-list
: an immutable list of exact non-negative integers that correspond to immutable fields for the structure type;super-struct-type
: a structure type descriptor for the most specific ancestor of the type that is controlled by the current inspector, or#f
if no ancestor is controlled by the current inspector;skipped?
:#f
if the seventh result is the most specific ancestor type or if the type has no supertype,#t
otherwise.
If the type for
struct-type
is not controlled by the current inspector, theexn:fail:contract
exception is raised.(struct-type-make-constructor
struct-type
)
returns a constructor procedure to create instances of the type forstruct-type
. If the type forstruct-type
is not controlled by the current inspector, theexn:fail:contract
exception is raised.(struct-type-make-predicate
struct-type
)
returns a predicate procedure to recognize instances of the type forstruct-type
. If the type forstruct-type
is not controlled by the current inspector, theexn:fail:contract
exception is raised.
4.6 Structures as Procedures
If the
prop:procedure
property is specified for a structure type
(see section 4.3),
instances of the new structure type are procedures. In particular,
when procedure?
is applied to the instance, the result
will be #t
. When an instance is used in the function
position of an application expression, a procedure is extracted from
the instance and used to complete the procedure call.
If the prop:procedure
property value is an integer,
it designates a field within the
structure that should contain a procedure. The
integer must be between 0 (inclusive) and
the third argument to make-struct-type
,
init-field-k
(exclusive). The designated field
must also be specified as immutable, so that after an instance of the structure is
created, its procedure cannot be changed. (Otherwise, the arity and
name of the instance could change, and such mutations are generally
not allowed for procedures.) When the instance is used as the
procedure in an application expression, the value of the designated
field in the instance is used to complete the procedure
call.12 That procedure receives all
of the arguments from the application expression. The procedure's
name (see section 6.2.3) and arity (see section 3.12.1) are
also used for the name and arity of the structure. If the value in
the designated field is not a procedure, then the instance behaves
like (case-lambda)
(i.e., a procedure which does not accept
any number of arguments).
Providing an integer proc-spec
argument to
make-struct-type
is the same as both supplying the value
with the
property and designating the field
as immutable (so that a property binding or immutable
designation is redundant and disallowed).prop:procedure
Example:
(define-values (struct:ap make-annotated-proc annotated-proc? ap-ref ap-set!) (make-struct-type
'annotated-proc #f 2 0 #fnull
#f 0)) (define (proc-annotation p) (ap-ref p 1)) (define plus1 (make-annotated-proc (lambda (x) (+ x 1)) "adds 1 to its argument")) (procedure?
plus1) ; =>#t
(annotated-proc? plus1) ; =>#t
(plus1 10) ; =>11
(proc-annotation plus1) ; =>"adds 1 to its argument"
When the prop:procedure
value is a procedure, it should accept at least one
argument. When an instance of the structure is used in an application
expression, the property-value procedure is called with the instance
as the first argument. The remaining arguments to the property-value
procedure are the arguments from the application expression. Thus, if
the application expression contained five arguments, the property-value procedure
is called with six arguments. The name of the instance (see
section 6.2.3) is unaffected by the property-value procedure, but the
instance's arity is determined by subtracting one from every possible
argument count of the property-value procedure. If the property-value procedure cannot accept
at least one argument, then the instance behaves like
(case-lambda)
.
Providing a procedure proc-spec
argument to
make-struct-type
is the same as supplying the value with the
property (so that a specific property
binding is disallowed).prop:procedure
(define-values (struct:fish make-fish fish? fish-ref fish-set!) (make-struct-type
'fish #f 2 0 #fnull
#f (lambda (f n) (fish-set! f 0 (+ n (fish-ref f 0)))))) (define (fish-weight f) (fish-ref f 0)) (define (fish-color f) (fish-ref f 1)) (define wanda (make-fish 12 'red)) (fish? wanda) ; =>#t
(procedure?
wanda) ; =>#t
(fish-weight wanda) ; =>12
(for-each
wanda '(1 2 3)) (fish-weight wanda) ; =>18
If a structure type generates procedure instances, then subtypes of
the type also generate procedure instances. The instances behave the
same as instances of the original type. When a
property or non-prop:procedure
#f
proc-spec
is
supplied to make-struct-type
with a supertype that already
behaves as a procedure,
the exn:fail:contract
exception is raised.
(procedure-struct-type?
struct-type
)
returns #t
if
instances of the structure type represented by struct-type
are
procedures (according to
), procedure?
#f
otherwise.
4.7 Structures as Synchronizable Events
The built-in
prop:evt
structure type property identifies structure types whose instances can
serve as synchronizable events; see section 7.7 for information on
synchronization and events.
The property value can be any of the following:
An event
evt
: In this case, using the structure as an event is equivalent to usingevt
.A procedure
proc
of one argument: In this case, the structure is similar to an event generated byguard-evt
, except that the would-be guard procedureproc
receives the structure as an argument, instead of no arguments.An exact, non-negative integer between
0
(inclusive) andinit-field-k
(exclusive): The integer identifies a field in the structure, and the field must be designated as immutable. If the field contains an object or an event-generating procedure of one argument, the event or procedure is used as above. Otherwise, the structure acts as an event that is never ready.
Instances of a structure type with the
or
prop:input-port
property (see section 4.8) is also
synchronizable by virtue of being a port. If the structure type has
more than one of prop:output-port
, prop:evt
, and
prop:input-port
, then the prop:output-port
value (if any)
takes precedence for determing the instance's behavior as an event,
and the prop:evt
property takes precedence over
prop:input-port
for synchronization.prop:output-port
Examples:
(define-values (struct:wt make-wt wt? wt-ref wt-set!) (make-struct-type
'wt #f 2 0 #f (list
(cons
prop:evt
0)) #f #f '(0))) (define sema (make-semaphore
)) (sync/timeout
0 (make-wt sema #f)) ; =>#f
(semaphore-post
sema) (sync/timeout
0 (make-wt sema #f)) ; =>sema
(semaphore-post
sema) (sync/timeout
0 (make-wt (lambda (self) (wt-ref self 1)) sema)) ; =>sema
(semaphore-post
sema) (define my-wt (make-wt (lambda (self) (wrap-evt (wt-ref self 1) (lambda (x) self))) sema)) (sync/timeout
0 my-wt) ; =>my-wt
(sync/timeout
0 my-wt) ; =>#f
4.8 Structures as Ports
The built-in
prop:input-port
and
prop:output-port
structure type properties identify structure types whose instances can
serve as input and output ports, respectively.
The property value can be any of the following:
An input port (for
) or output port (forprop:input-port
): In this case, using the structure as port is equivalent to using the given one.prop:input-port
An exact, non-negative integer between
0
(inclusive) andinit-field-k
(exclusive): The integer identifies a field in the structure, and the field must be designated as immutable. If the field contains an input port (for
) or output port (forprop:input-port
), the port is used. Otherwise, an empty string input port is used forprop:input-port
, and a port that discards all data is used forprop:input-port
.prop:output-port
Some procedures, such as
, work on both input
and output ports. When given an instance of a structure type with
both the file-position
prop:input-port
and prop:output-port
properties, the instance is used as an input port.
4.9 Structure Utilities
The following utility procedures work on all structure instances:
(struct->vector
v
[opaque-v
])
creates a vector representingv
. The first slot of the result vector contains a symbol of the formstruct:
. Each remaining slot contains either the value of a field ins
v
if it is accessible via the current inspector, oropaque-v
for a field that is not accessible. A singleopaque-v
value is used in the vector for contiguous inaccessible fields. (Consequently, the size of the vector does not match the size of thestruct
if more than one field is inaccessible.) The symbol'...
is the default value foropaque-v
.(struct?
v
)
returns#t
ifstruct->vector
exposes any fields ofv
with the current inspector,#f
otherwise.
Two structure values are eqv?
if and only if they are
eq?
. Two structure values are equal?
if and
only if they are instances of the same structure type, no fields are
opaque, and the results of applying
to the
structs are struct->vector
. (Consequently, equal?
testing
for structures depends on the current inspector.)equal?
Each kind of value returned by define-struct
and
has a recognizing predicate:
make-struct-type
(struct-type?
v
)
returns#t
ifv
is a structure type descriptor value,#f
otherwise.(struct-constructor-procedure?
v
)
returns#t
ifv
is a constructor procedure generated bydefine-struct
or
,make-struct-type
#f
otherwise.(struct-predicate-procedure?
v
)
returns#t
ifv
is a predicate procedure generated bydefine-struct
or
,make-struct-type
#f
otherwise.(struct-accessor-procedure?
v
)
returns#t
ifv
is an accessor procedure generated bydefine-struct
,
, ormake-struct-type
,make-struct-field-accessor
#f
otherwise.(struct-mutator-procedure?
v
)
returns#t
ifv
is a mutator procedure generated bydefine-struct
,
, ormake-struct-type
,make-struct-field-mutator
#f
otherwise.
12 This procedure can be another structure that acts as a procedure. The immutability of procedure fields disallows cycles in the procedure graph, so that the procedure call will eventually continue with a non-structure procedure.