A structure is a record with named fields. struct defines a structure type together with a constructor, a predicate and an accessor for each field, and it is the usual way to make a new data type in Racket.
The struct form
(struct id maybe-super (field …) struct-option …)
maybe-superis nothing or the name of another structure type; the new type then adds fields to the supertype's.- each
fieldisfield-idor[field-id field-option …]; the field options are#:mutableand#:auto(the field is not a constructor argument and takes the value of#:auto-value, default#f).
With n fields it defines up to 4 + 2n names:
| Name | Is |
|---|---|
struct:id | the structure type descriptor, a value that stands for the type |
id | the constructor, taking one argument for each field without #:auto; also (as a syntax binding) the name for match, struct-copy and subtypes |
id? | the predicate: true for instances of the type and of its subtypes |
id-field | an accessor for each field |
set-id-field! | a mutator for each mutable field, returning void |
The options
| Option | Effect |
|---|---|
#:mutable | every field is mutable (a field may also carry the option itself, but not both) |
#:super super-expr | the supertype comes from an expression that produces a structure type descriptor, instead of a name before the fields |
#:inspector inspector-expr | sets the inspector that controls who can see inside the structure; the default makes the structure opaque |
#:transparent | short for #:inspector #f: anyone can inspect it, so equal? compares fields, and printing shows them |
#:prefab | uses or makes a prefab structure type, fixed by its name and shape, that can be written and read back as a #s(…) literal |
#:sealed | the type has no subtypes (added in version 8.0.0.7) |
#:authentic | the type cannot be impersonated or chaperoned, for speed (added in 6.9.0.4) |
#:auto-value expr | the value of #:auto fields |
#:guard expr | a procedure that checks or converts the field values at construction, and receives the type name as its last argument |
#:property prop-expr val-expr | attaches a structure-type property and its value; may repeat |
#:properties prop-list-expr | several properties as an association list (added in 8.17.0.4) |
#:methods gen:name (definition …) | implements the methods of a generic interface (define-generics) |
#:name name-id, #:extra-name name-id | binds a different name (or one more) as the syntax binding of the type |
#:constructor-name id, #:extra-constructor-name id | binds the constructor to a different name (or one more) than the type's |
#:reflection-name symbol-expr | the name that error messages and printing use |
#:omit-define-syntaxes, #:omit-define-values | skips defining the syntax binding, or the values (constructor, predicate and the rest) |
#lang racket
(struct posn (x y) #:transparent)
(struct posn3 posn (z)) ; a subtype with one more field
(define p (posn 1 2))
(posn-x p) ; 1
(posn? (posn3 1 2 3)) ; #t: a subtype is a posn
(equal? (posn 1 2) (posn 1 2)) ; #t, because it is transparent
(struct counter ([n #:mutable]))
(define c (counter 0))
(set-counter-n! c (add1 (counter-n c)))
(struct celsius (degrees)
#:guard (λ (d name)
(unless (real? d) (error name "not a number: ~e" d))
d))
(struct point (x y) #:prefab)
(point 1 2) ; prints as #s(point 1 2)
(struct-copy posn p [x 10]) ; a copy with x changed
Copying and reflection
(struct-copy id struct-expr [field-id expr] …)makes a new instance with some fields changed;[field-id #:parent parent-id expr]reaches a field of a parent type.(struct->vector s)turns a transparent structure into a vector headed bystruct:name.struct-info,struct-type-info,make-struct-type,make-struct-field-accessorandmake-struct-field-mutatorare the procedures behindstruct:make-struct-typemakes a type from a name, a supertype, field counts, an auto value, properties, an inspector and a guard.- An opaque structure (no
#:transparent) prints as#<name>and isequal?only to itself;struct->vectorshows only'#(struct:name ...). define-structis the older form:(define-struct name (field …) option …)names the constructormake-name.structis preferred.
Prefab structures
A prefab type is defined by its name, number of fields, mutability and supertype alone, so two modules that use the same description share the type, and a value can be written to a file and read back. It is written #s(name field …). When the type has a supertype, automatic fields or mutable fields, the first datum is a list instead of a name, the prefab key; the shapes that make-prefab-struct accepts are not listed here. A prefab structure cannot have a guard or properties, and the read-syntax reader accepts only immutable ones. prefab-struct-key, make-prefab-struct and prefab-key->struct-type work on them.
Properties and generic interfaces
A structure type property adds behaviour. The built-in ones whose documentation I read include:
| Property | Gives the structure |
|---|---|
prop:procedure | the ability to be called as a procedure (the value is a procedure or a field index) |
prop:equal+hash | its own equal? and hash functions |
prop:sequence, prop:stream | iteration with for, or a lazy stream |
prop:custom-write, prop:custom-print-quotable | its own printed form |
prop:struct-info, prop:struct-auto-info, prop:struct-field-info | structure information for macros that take part in match or struct-copy |
prop:match-expander, prop:legacy-match-expander | use of the name as a custom match pattern |
prop:contract, prop:flat-contract, prop:chaperone-contract, prop:contracted, prop:blame, prop:collapsible-contract | a contract or blame object made from a structure |
prop:exn:srclocs, prop:exn:missing-module | source locations for an exception; the module name for a missing-module error |
prop:sealed, prop:arity-string, prop:checked-procedure, prop:place-location | a sealed type; an arity description in errors; a checked procedure; a place-aware location |
A generic interface (racket/generic) is a set of methods that a structure can implement with #:methods. (define-generics shape (area shape) (name shape)) defines the interface gen:shape and generic functions area and name; (struct circle (r) #:methods gen:shape [(define (area c) (* 3.14 (circle-r c) (circle-r c)))]) implements it. The library defines several: gen:equal+hash, gen:custom-write, gen:dict, gen:set, gen:stream, gen:ordered-dict.
Not described
Known, not yet covered
- Reference: inspectors (
make-inspector,current-inspector,struct-infoaccess rules), the full set of prefab key shapes,make-struct-type's arguments in detail, the structure-info protocol for macros (racket/struct-info),struct/derived,struct/contract(astructwith contracts on its fields) and#:nameand#:extra-name's exact rules were not described. The list of properties is a sample, not the complete list inracket/base. - Reference:
gen:dict,gen:set,gen:ordered-dictandgen:custom-writeare named from the library without having been read.
See also
- Pattern matching – taking a structure apart with
match - Classes and objects – the other way to define a type with behaviour
- Typed Racket –
structwith typed fields - Scheme: records –
define-record-type, the Scheme equivalent