New types can be constructed using the define-record-type
macro
from the define-record-types
structure
The general syntax is:
This makes the following definitions:(define-record-typetag
type-name
(constructor-name
field-tag
...)predicate-name
(field-tag
accessor-name
[modifier-name
]) ...)
| type |
(constructor-name
field-init ...) -> type-name
(predicate-name
value) -> boolean
(accessor-name
type-name) -> value
(modifier-name
type-name value)
Type-name
is the record type itself, and can be used to
specify a print method (see below).
Constructor-name
is a constructor that accepts values
for the fields whose tags are specified.
Predicate-name
is a predicate that returns #t
for
elements of the type and #f
for everything else.
The accessor-name
s retrieve the values of fields,
and the modifier-name
's update them.
Tag
is used in printing instances of the record type and
the field-tag
s are used in the inspector and to match
constructor arguments with fields.
Define-record-discloser
determines how
records of type type
are printed.
Discloser
should be procedure which takes a single
record of type type
and returns a list whose car is
a symbol.
The record will be printed as the value returned by discloser
with curly braces used instead of the usual parenthesis.
For example
defines(define-record-type pare :pare (kons x y) pare? (x kar set-kar!) (y kdr))
kons
to be a constructor, kar
and kdr
to be
accessors, set-kar!
to be a modifier, and pare?
to be a predicate
for a new type of object.
The type itself is named :pare
.
Pare
is a tag used in printing the new objects.
By default, the new objects print as #{Pare}
.
The print method can be modified using define-record-discloser
:
will cause the result of(define-record-discloser :pare (lambda (p) `(pare ,(kar p) ,(kdr p))))
(kons 1 2)
to print as
#{Pare 1 2}
.
Define-record-resumer
can be used to control how records are stored in heap images.
Records are implemented using primitive objects exactly analogous
to vectors.
Every record has a record type (which is another record) in the first slot.
Note that use of these procedures, especially record-set!
, breaks
the record abstraction described above; caution is advised.
These procedures are in the structure records
.
(make-record n value) -> record
(record value ...) -> record-vector
(record? value) -> boolean
(record-length record) -> integer
(record-type record) -> value
(record-ref record i) -> value
(record-set! record i value)
vector-
procedures except that they
operate on records.
The value returned by record-length
includes the slot holding the
record's type.
(record-type x
)
is equivalent to (record-ref x
0)
.
Record types are themselves records of a particular type (the first slot
of :record-type
points to itself).
A record type contains four values: the name of the record type, a list of
the names its fields, and procedures for disclosing and resuming records
of that type.
Procedures for manipulating them are in the structure record-types
.
(make-record-type name field-names) -> record-type
(record-type? value) -> boolean
(record-type-name record-type) -> symbol
(record-type-field-names record-type) -> symbols
(record-constructor record-type field-names) -> procedure
(record-predicate record-type) -> procedure
(record-accessor record-type field-name) -> procedure
(record-modifier record-type field-name) -> procedure
Record-constructor
returns a constructor that is passed the initial
values for the fields specified and returns a new record.
Record-predicate
returns a predicate that return true when passed
a record of type record-type
and false otherwise.
Record-accessor
and record-modifier
return procedures that
reference and set the given field in records of the approriate type.
Record-types
is the initial exporter of
define-record-discloser
(re-exported by define-record-types
described above)
and
define-record-resumer
(re-exported by
external-calls
).
The procedures described in this section can be used to define new record-type-defining macros.
is (sematically) equivalent to(define-record-type pare :pare (kons x y) pare? (x kar set-kar!) (y kdr))
(define :pare (make-record-type 'pare '(x y))) (define kons (record-constructor :pare '(x y))) (define kar (record-accessor :pare 'x)) (define set-kar! (record-modifier :pare 'x)) (define kdr (record-accessor :pare 'y))
The "(semantically)" above is because define-record-type
adds
declarations, which allows the type checker to detect some misuses of records,
and uses more efficient definitions for the constructor, accessors, and
modifiers.
Ignoring the declarations, which will have to wait for another edition of
the manual, what the above example actually expands into is:
(define :pare (make-record-type 'pare '(x y))) (define (kons x y) (record :pare x y)) (define (kar r) (checked-record-ref r :pare 1)) (define (set-kar! r new) (checked-record-set! r :pare 1 new)) (define (kdr r) (checked-record-ref r :pare 2))
Checked-record-ref
and Checked-record-set!
are
low-level procedures that check the type of the
record and access or modify it using a single VM instruction.
Previous: Arrays | Next: Finite record types