A record is a value with a fixed number of named fields, whose type is distinct from every other type. R5RS had none; SRFI 9 (1999) defined define-record-type, R6RS defined a richer form with the same name, and R7RS-small took the SRFI 9 form. The two forms differ, and the viewer tells them apart by what follows the type name.
define-record-type of SRFI 9 and R7RS
(define-record-type name constructor pred field ...) defines a record type and its operations. It is generative: each use makes a new type, distinct from all others, even one of the same name.
| Part | Form | Defines |
|---|---|---|
| name | an identifier, often written <name> | bound to a representation of the type (run-time or syntactic; the report does not use it) |
| constructor | (constructor-name field-name ...) | a procedure that takes as many arguments as it lists fields and returns a new record; the other fields are unspecified. Some implementations accept a bare identifier, or #f |
| pred | an identifier | a predicate, true of records made by this type’s constructor |
| field | (field-name accessor) or (field-name accessor modifier) | an accessor that takes a record and returns the field, and a modifier that takes a record and a value (result unspecified). A field name can appear only once, and so can an accessor or modifier name |
(define-record-type <pare>
(kons x y)
pare?
(x kar set-kar!)
(y kdr))
(pare? (kons 1 2)) ; #t
(pare? (cons 1 2)) ; #f
(kar (kons 1 2)) ; 1
(kdr (kons 1 2)) ; 2
(let ((k (kons 1 2)))
(set-kar! k 3)
(kar k)) ; 3
Passing an accessor a value that is not a record of its type is an error. R7RS records are disjoint from every other type, as are the records of two different define-record-type forms. Records may be printed by write as the implementation chooses.
define-record-type of R6RS
R6RS has three libraries for records: (rnrs records syntactic), a procedural layer (rnrs records procedural) and (rnrs records inspection).
The syntactic form is (define-record-type name-spec record-clause ...). The name spec is either (record-name constructor-name predicate-name), or a bare record-name, in which case the constructor is make-record-name and the predicate is record-name?. The clauses, each allowed once, are:
| Clause | Meaning |
|---|---|
(fields field-spec ...) | The fields. A spec is (immutable name accessor), (mutable name accessor mutator), (immutable name), (mutable name) or just name. When the accessor is not named it is record-name-field-name, and the mutator is that with -set! added: frob-widget and frob-widget-set!. A bare name is an immutable field |
(parent parent-name) | The record type extends another, whose name is given. Each record of the new type is also a record of the parent type, and the parent’s accessors work on it. A base type has no parent. Single inheritance, transitive |
(parent-rtd rtd constructor-descriptor) | The parent given as run-time values, for record types made by the procedural layer |
(protocol expression) | A procedure from a constructor of the parent to a constructor of this type, which lets the constructor compute field values or take different arguments. Without it the default constructor takes all fields, parent fields first |
(sealed #t) | The type cannot be extended |
(opaque #t) | Records of the type cannot be examined with the inspection library, so it can enforce an abstraction barrier |
(nongenerative [uid]) | The type is identified by a unique id: another definition with the same id and a compatible shape gives the same type, rather than a new one. Without uid one is generated |
(define-record-type (point make-point point?)
(fields (immutable x point-x)
(mutable y point-y set-point-y!))
(sealed #t))
(define-record-type (point3 make-point3 point3?)
(parent point)
(fields (immutable z point3-z))
(protocol
(lambda (n)
(lambda (x y z) ((n x y) z)))))
A record type is immutable if it has no mutable field, and then so are its records. For two records eqv? is true only if they came from one call of a constructor. The viewer reads the clause words (fields, parent, protocol, sealed, opaque, nongenerative, mutable, immutable) as syntax in this form.
Procedural layer
make-record-type-descriptor name parent uid sealed? opaque? fields makes a record-type descriptor (rtd), record-type-descriptor? tests one, make-record-constructor-descriptor rtd parent-constructor-descriptor protocol makes a constructor descriptor, and record-constructor, record-predicate, record-accessor and record-mutator make the operations from them. The syntactic layer’s name is also a handle: (record-type-descriptor name) and (record-constructor-descriptor name) give the rtd and the descriptor of a record type that is defined in scope.
Inspection
(rnrs records inspection) has record?, record-rtd, record-type-name, record-type-parent, record-type-uid, record-type-generative?, record-type-sealed?, record-type-opaque?, record-type-field-names and record-field-mutable?. They fail on records of an opaque type.
Other record systems
| System | What it is |
|---|---|
| SRFI 9 | The base form, in (srfi 9); a report-level feature of R7RS |
| SRFI 57, 99, 131, 136, 150, 237, 240 | Proposals and libraries that extend records with inheritance, field defaults, syntax of R6RS records in R7RS, or both forms at once; SRFI lists the titles. SRFI 99 is “ERR5RS Records”, and SRFI 237 “R6RS Records (refined)”; SRFI 240 reconciles the two forms |
define-structure | MIT/GNU Scheme: (define-structure (point options) x y) defines make-point, point-x, set-point-x! and point? |
define-record | CHICKEN’s define-record, and its define-record-type with extensions |
| classes | Guile’s GOOPS (define-class), Gauche’s object system, STklos and SCM’s; these have generic functions and are not records |
struct | Racket’s structures, which are not Scheme records but the closest relation |
The viewer colours <name>-style identifiers as types wherever they occur, and the names defined by a define-record-type by role.
Not described
Known, not yet covered
- Reference: the exact rules of the R6RS protocol (how a child’s protocol receives the parent’s constructor, and the default constructor descriptor) and of nongenerative types with a uid are summarised and not described.
- Reference: the SRFIs of the table were not read, so their differences are not described; the entry for
define-structureanddefine-recordis from memory of the MIT/GNU and CHICKEN manuals, which were not read. - Reference: printing of records by
writein each implementation is not described.
See also
- Data types – records among the disjoint types
- Error handling – R6RS conditions are records
- Standard libraries – where each record library lives