racket/collects/scribblings/guide/define-struct.scrbl
Matthew Flatt 2fe7c75dc1 doc work
svn: r6692
2007-06-19 08:32:38 +00:00

152 lines
5.1 KiB
Racket

#reader(lib "docreader.ss" "scribble")
@require[(lib "manual.ss" "scribble")]
@require[(lib "eval.ss" "scribble")]
@require[(lib "bnf.ss" "scribble")]
@require["guide-utils.ss"]
@title[#:tag "guide:define-struct"]{Programmer-Defined Datatypes}
This section introduces the @scheme[define-struct] form for creating
your own datatypes. The class-based object system offers an alternate
mechanism for creating new datatypes; the resulting objects are
nevertheless implemented as structures, and we defer discussion of
objects to @secref["classes"].
@; ------------------------------------------------------------
@section{Simple Structure Types: @scheme[define-struct]}
To a first approximation, the syntax of @scheme[define-struct] is
@specform[
(define-struct struct-id (field-id ...))
]{}
Such a definition binds @scheme[_struct-id], but only to static
information about the structure type that cannot be used directly:
@def+int[
(define-struct posn (x y))
posn
]
We explain one use of the @scheme[_struct-id] binding in the next
section.
In addition to defining @scheme[_struct-id], however,
@scheme[define-struct] also defines a number of functions whose names
are built from @scheme[_struct-id] and the @scheme[_field-id]s:
@itemize{
@item{@schemeidfont{make-}@scheme[_struct-id] : a
@defterm{constructor} function that takes as many arguments as
the number of @scheme[_field-id]s, and returns an instance of
the structure type.
@examples[(make-posn 1 2)]}
@item{@scheme[_struct-id]@schemeidfont{?} : a @defterm{predicate}
function that takes a single argument and returns @scheme[#t]
if it is an instance of the structure type, @scheme[#f]
otherwise.
@examples[(posn? 3) (posn? (make-posn 1 2))]}
@item{@scheme[_struct-id]@schemeidfont{-}@scheme[_field-id] : for
each @scheme[_field-id], an @defterm{accessor} that extracts
the value of the corresponding field from an instance of the
structure type.
@examples[(posn-x (make-posn 1 2)) (posn-y (make-posn 1 2))]}
@item{@schemeidfont{set-}@scheme[_struct-id]@schemeidfont{-}@scheme[_field-id]@schemeidfont{!} : for
each @scheme[_field-id], a @defterm{mutator} that sets
the value of the corresponding field in an instance of the
structure type.
@examples[(define p (make-posn 1 2))
(posn-x p)
(set-posn-x! p 10)
(posn-x p)]}
}
A @scheme[define-struct] form places no constraints on the kinds of
values that can appear for fields in an instance of the structure
type. For example, @scheme[(make-posn "apple" #f)] produces an
instance of @scheme[posn], even though @scheme["apple"] and
@scheme[#f] are not valid coordinates for the obvious uses of
@scheme[posn] instances. Enforcing constraints on field values, such
as requiring them to be numbers, is the job of a contract, as
discussed later in @secref["guide:contracts"].
@; ------------------------------------------------------------
@section{Structure Subtypes}
An extended form of @scheme[defin-struct] can be used to define a
@defterm{structure subtype}, which is a structure type that extends an
existing structure type:
@specform[
(define-struct (struct-id super-id) (field-id ...))
]
The @scheme[_super-id] must be a structure type name bound by
@scheme[define-struct] (i.e., the name bound by @scheme[define-struct]
that cannot be used directly as an expression).
@as-examples[@schemeblock+eval[
(define-struct posn (x y))
(define-struct (3d-posn posn) (z))
]]
A structure subtype inherits the fields of its supertype, and the
subtype constructor accepts the values for the subtype fields after
values for the supertype fields. An instance of a structure subtype
can be used with the predicate, accessor, and mutator fields of the
supertype.
@examples[
(define p (make-3d-posn 1 2 3))
p
(posn? p)
(posn-x p)
(3d-posn-z p)
]
@; ------------------------------------------------------------
@section{Opaque versus Transparent Stucture Types}
With a structure type definition like
@schemeblock[
(define-struct posn (x y))
]
an instance of the structure type prints in a way that does not show
any information about the fields values. That is, structure types by
default are @defterm{opaque}. If the accessors and mutators of a
structure type are kept private to a module, then no other module can
rely on the representation of the type's instances.
To make a structure type @defterm{transparent}, use the
@scheme[#:inspector] keyword with the value @scheme[#f] after the
field-name sequence:
@def+int[
'(define-struct posn (x y)
#:inspector #f)
(make-posn 1 2)
]
An instance of a transparent structure type prints like a vector, and
it shows the content of the structure's fields. A transparent
structure type also allows reflective operations, like
@scheme[struct?] and @scheme[struct-info], to be used on its
instances (see @secref["reflection"]).
Structure types are opaque by default, because opaque structure
instances provide more encapsulation guarantees. That is, a library
can use an opaque structure to encapsulate data, and clients of the
library cannot manipulate the data in the structure except as allowed
by the library.