Scheme 48 Manual -- Records

Scheme 48 Manual | Contents | In Chapter: Libraries
Previous: Arrays | Next: Finite record types

## Records

New types can be constructed using the `define-record-type` macro from the `define-record-types` structure The general syntax is:

>     (define-record-type tag type-name
>       (constructor-name field-tag ...)
>       predicate-name
>       (field-tag accessor-name [modifier-name])
>       ...)

This makes the following definitions:

- ``type-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 type discloser)`

`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

>     (define-record-type pare :pare
>       (kons x y)
>       pare?
>       (x kar set-kar!)
>       (y kdr))

defines `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`:

>     (define-record-discloser :pare
>       (lambda (p) `(pare ,(kar p) ,(kdr p))))

will cause the result of `(kons 1 2)` to print as `#{Pare 1 2}`.

`Define-record-resumer` can be used to control how records are stored in heap images.

### Low-level access to records

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)`

These the same as the standard `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

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`

These procedures construct the usual record-manipulating procedures. `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.

- `(define-record-discloser record-type discloser)`
- `(define-record-resumer record-type resumer)`

`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.

>     (define-record-type pare :pare
>       (kons x y)
>       pare?
>       (x kar set-kar!)
>       (y kdr))

is (sematically) equivalent to

>     (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