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 fields defines 4 + 2n names:

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 make-struct-type procedure creates a new structure type in the same way as the define-struct form of section 4.1, but provides a more general interface. In particular, the make-struct-type procedure supports structure type properties.

Unlike define-struct, make-struct-type returns a single accessor procedure and a single mutator procedure for all fields. The make-struct-field-accessor and make-struct-field-mutator procedures convert a type-specific accessor or mutator returned by make-struct-type into a field-specific accessor or mutator:

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 #f null (make-inspector) #f null
                    ;; 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 make-struct-type 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-property name-symbol [guard-proc]) creates a new structure type property and returns three values:

If the optional guard-proc is supplied, it is called by make-struct-type before attaching the property to a new structure type. The guard-proc must accept two arguments: a value for the property supplied to make-struct-type, and a list containing information about the new structure type. The list contains the values that 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 make-struct-type. 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.

(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 current-inspector parameter determines a default inspector argument for new structure types. An alternate inspector can be provided though the optional inspector-expr expression of the define-struct form (see section 4.1), as shown above, or through an optional inspector argument to make-struct-type (see section 4.3).

(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 struct-info and struct-type-info procedures provide inspector-based access to structure and structure type information:

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 prop:procedure property and designating the field as immutable (so that a property binding or immutable designation is redundant and disallowed).

Example:

(define-values (struct:ap make-annotated-proc annotated-proc? ap-ref ap-set!) 
  (make-struct-type 'annotated-proc #f 2 0 #f null #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 prop:procedure property (so that a specific property binding is disallowed).

(define-values (struct:fish make-fish fish? fish-ref fish-set!) 
  (make-struct-type 'fish #f 2 0 #f null #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 prop:procedure property or non-#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:

Instances of a structure type with the prop:input-port or prop:output-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:evt, prop:input-port, and prop:output-port, then the prop:evt value (if any) takes precedence for determing the instance's behavior as an event, and the prop:input-port property takes precedence over prop:output-port for synchronization.

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:

Some procedures, such as file-position, work on both input and output ports. When given an instance of a structure type with both the 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:

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 struct->vector to the structs are equal?. (Consequently, equal? testing for structures depends on the current inspector.)

Each kind of value returned by define-struct and make-struct-type has a recognizing predicate:


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.