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-typetagtype-name(constructor-namefield-tag...)predicate-name(field-tagaccessor-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-names 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-tags 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