#lang scribble/doc @(require "mz.ss" (for-syntax racket/base) (for-label racket/serialize)) @(define posn-eval (make-base-eval)) @(interaction-eval #:eval posn-eval (require (for-syntax racket/base))) @title[#:tag "define-struct"]{Defining Structure Types: @racket[struct]} @guideintro["define-struct"]{@racket[struct]} @defform/subs[(struct id maybe-super (field ...) struct-option ...) ([maybe-super code:blank super-id] [field field-id [field-id field-option ...]] [struct-option #:mutable (code:line #:super super-expr) (code:line #:inspector inspector-expr) (code:line #:auto-value auto-expr) (code:line #:guard guard-expr) (code:line #:property prop-expr val-exr) (code:line #:transparent) (code:line #:prefab) (code:line #:constructor-name constructor-id) (code:line #:extra-constructor-name constructor-id) #:omit-define-syntaxes #:omit-define-values] [field-option #:mutable #:auto])]{ Creates a new @techlink{structure type} (or uses a pre-existing structure type if @racket[#:prefab] is specified), and binds transformers and variables related to the @tech{structure type}. A @racket[struct] form with @math{n} @racket[field]s defines up to @math{4+2n} names: @itemize[ @item{@racketidfont{struct:}@racket[id], a @deftech{structure type descriptor} value that represents the @tech{structure type}.} @item{@racket[constructor-id] (which defaults to @racket[id]), a @deftech{constructor} procedure that takes @math{m} arguments and returns a new instance of the @tech{structure type}, where @math{m} is the number of @racket[field]s that do not include an @racket[#:auto] option.} @item{@racket[id]@racketidfont{?}, a @deftech{predicate} procedure that returns @racket[#t] for instances of the @tech{structure type} (constructed by @racket[constructor-id] or the @tech{constructor} for a subtype) and @racket[#f] for any other value.} @item{@racket[id]@racketidfont{-}@racket[field-id], for each @racket[field]; an @deftech{accessor} procedure that takes an instance of the @tech{structure type} and extracts the value for the corresponding field.} @item{@racketidfont{set-}@racket[id]@racketidfont{-}@racket[field-id]@racketidfont{!}, for each @racket[field] that includes a @racket[#:mutable] option, or when the @racket[#:mutable] option is specified as a @racket[struct-option]; a @deftech{mutator} procedure that takes an instance of the @tech{structure type} and a new field value. The structure is destructively updated with the new value, and @|void-const| is returned.} @item{@racket[id], a @tech{transformer binding} that encapsulates information about the structure type declaration. This binding is used to define subtypes, and it also works with the @racket[shared] and @racket[match] forms. For detailed information about the binding of @racket[id], see @secref["structinfo"]. The @racket[constructor-id] and @racket[id] can be the same, in which case @racket[id] performs both roles.} ] If @racket[super-id] is provided, it must have a transformer binding of the same sort bound to @racket[id] (see @secref["structinfo"]), and it specifies a supertype for the structure type. Alternately, the @racket[#:super] option can be used to specify an expression that must produce a @tech{structure type descriptor}. See @secref["structures"] for more information on structure subtypes and supertypes. If both @racket[super-id] and @racket[#:super] are provided, a syntax error is reported. If the @racket[#:mutable] option is specified for an individual field, then the field can be mutated in instances of the structure type, and a @tech{mutator} procedure is bound. Supplying @racket[#:mutable] as a @racket[struct-option] is the same as supplying it for all @racket[field]s. If @racket[#:mutable] is specified as both a @racket[field-option] and @racket[struct-option], a syntax error is reported. The @racket[#:inspector], @racket[#:auto-value], and @racket[#:guard] options specify an inspector, value for automatic fields, and guard procedure, respectively. See @racket[make-struct-type] for more information on these attributes of a structure type. The @racket[#:property] option, which is the only one that can be supplied multiple times, attaches a property value to the structure type; see @secref["structprops"] for more information on properties. The @racket[#:transparent] option is a shorthand for @racket[#:inspector #f]. @margin-note{Use the @racket[prop:procedure] to property implement an @as-index{applicable structure}, use @racket[prop:evt] to create a structure type whose instances are @tech{synchronizable events}, and so on. By convention, property names start with @racketidfont{prop:}.} The @racket[#:prefab] option obtains a @techlink{prefab} (pre-defined, globally shared) structure type, as opposed to creating a new structure type. Such a structure type is inherently transparent and cannot have a guard or properties, so using @racket[#:prefab] with @racket[#:transparent], @racket[#:inspector], @racket[#:guard], or @racket[#:property] is a syntax error. If a supertype is specified, it must also be a @tech{prefab} structure type. If @racket[constructor-id] is supplied, then the @tech{transformer binding} of @scheme[id] records @scheme[constructor-id] as the constructor binding; as a result, for example, @scheme[struct-out] includes @racket[constructor-id] as an export. If @racket[constructor-id] is supplied via @racket[#:extra-constructor-name] and it is not @racket[id], applying @racket[object-name] on the constructor produces the symbolic form of @racket[id] rather than @racket[constructor-id]. If @racket[constructor-id] is supplied via @racket[#:constructor-name] and it is not the same as @racket[id], then @racket[id] does not serve as a constructor, and @racket[object-name] on the constructor produces the symbolic form of @racket[constructor-id]. If the @racket[#:omit-define-syntaxes] option is supplied, then @racket[id] is not bound as a transformer. If the @racket[#:omit-define-values] option is supplied, then none of the usual variables are bound, but @racket[id] is bound. If both are supplied, then the @racket[struct] form is equivalent to @racket[(begin)]. If @racket[#:auto] is supplied as a @racket[field-option], then the @tech{constructor} procedure for the structure type does not accept an argument corresponding to the field. Instead, the structure type's automatic value is used for the field, as specified by the @racket[#:auto-value] option, or as defaults to @racket[#f] when @racket[#:auto-value] is not supplied. The field is mutable (e.g., through reflective operations), but a mutator procedure is bound only if @racket[#:mutable] is specified. If a @racket[field] includes the @racket[#:auto] option, then all fields after it must also include @racket[#:auto], otherwise a syntax error is reported. If any @racket[field-option] or @racket[struct-option] keyword is repeated, other than @racket[#:property], a syntax error is reported. For serialization, see @racket[define-serializable-struct]. @defexamples[ #:eval posn-eval (struct posn (x y [z #:auto]) #:auto-value 0 #:transparent) (posn 1 2) (posn? (posn 1 2)) (posn-y (posn 1 2)) ] @defs+int[ #:eval posn-eval [(struct color-posn posn (hue) #:mutable) (define cp (color-posn 1 2 "blue"))] (color-posn-hue cp) cp (set-posn-z! cp 3) ]} @defform[(struct-field-index field-id)]{ This form can only appear as an expression within a @racket[struct] form; normally, it is used with @racket[#:property], especially for a property like @racket[prop:procedure]. The result of a @racket[struct-field-index] expression is an exact, non-negative integer that corresponds to the position within the structure declaration of the field named by @racket[field-id]. @defexamples[ #:eval posn-eval (struct mood-procedure (base rating) #:property prop:procedure (struct-field-index base)) (define happy+ (mood-procedure add1 10)) (happy+ 2) (mood-procedure-rating happy+) ]} @defform/subs[(define-struct id-maybe-super (field ...) struct-option ...) ([id-maybe-super id (id super-id)])]{ Like @racket[struct], except that the syntax for supplying a @racket[super-id] is different, and a @racket[_constructor-id] that is a @racketidfont{make-} prefix on @racket[id] is implicitly supplied via @racket[#:extra-constructor-name]. This form is provided for backward compatibility; @racket[struct] is preferred. @defexamples[ #:eval posn-eval (define-struct posn (x y [z #:auto]) #:auto-value 0 #:transparent) (make-posn 1 2) (posn? (make-posn 1 2)) (posn-y (make-posn 1 2)) ]} @defform[(define-struct/derived (id . rest-form) id-maybe-super (field ...) struct-option ...)]{ Like @racket[define-struct], but intended for use by macros that expand to @racket[define-struct]. The form immediately after @racket[define-struct/derived] is used for all syntax-error reporting, and the only constraint on the form is that it starts with some @racket[id]. @defexamples[ #:eval posn-eval (define-syntax (define-xy-struct stx) (syntax-case stx () [(ds name . rest) (with-syntax ([orig stx]) #'(define-struct/derived orig name (x y) . rest))])) (define-xy-struct posn) (posn-x (make-posn 1 2)) (define-xy-struct posn #:mutable) (set-posn-x! (make-posn 1 2) 0) (define-xy-struct posn #:bad-option) ]} @; ---------------------------------------- @close-eval[posn-eval]