Records

A record is a data structure with a fixed number of components, which are called the fields of the record. Each field has a name, and access to that particular field of a record typically uses that name in some way. Ideally, the structure should provide random access to each field; in other words, one should be able to inspect the contents of any field, independently, without having to access any of the others.

For instance, an astronomical database that keeps track of information about various stars might assemble the data into records, one record for each star. The fields of such a record might include name, right-ascension, declination, visual-magnitude, spectral-type, and so on. Similarly, a library's card catalog includes a record for each book or other item that the library holds, with fields like author, title, imprint, call-number, and checked-out?.

Scheme does not provide any built-in record types, so the programmer has to define her own. Fortunately, this is straightforward. To create a new record type foo in Scheme, one should define a procedure to carry out each of the following operations:

There's an obvious analogy between this collection of procedures and the provision that standard Scheme makes for its built-in data structures, such as pairs: The cons procedure is the pair constructor, car and cdr are the selectors, and pair? is the type predicate. One could even think of a pair as a record containing two fields.

To implement a record type in standard Scheme, one has to select an existing type and figure out how to perform the record operations using only values of the existing type. There are various ways to do this. One might, for instance, implement a record as a list in which the elements are a type tag and the successive field values, in some fixed order, thus:

;; MAKE-STAR, the constructor of STAR records

(define (make-star name right-ascension declination visual-magnitude spectral-type)
  (list 'star name right-ascension declination visual-magnitude spectral-type))

;; Field selectors for STAR records

(define (star-name stella)
  (list-ref stella 1))

(define (star-right-ascension stella)
  (list-ref stella 2))

(define (star-declination stella)
  (list-ref stella 3))

(define (star-visual-magnitude stella)
  (list-ref stella 4))

(define (star-spectral-type stella)
  (list-ref stella 5))

;; STAR?, the type predicate for STAR records

(define (star? object)
  (and (pair? object)
       (eq? (car object) 'star)
       (list? (cdr object))
       (= (length (cdr object)) 5)))

Here are some examples of the use of these procedures. First, let's construct a star and store it in a variable:

> (define Algol (make-star "Beta Persei" 3.13 40.93 2.66 'B8))

Print it back out immediately, just to confirm that the fields got filled in correctly.

> Algol
(star "Beta Persei" 3.13 40.93 2.66 b8)

Examine a couple of its fields separately.

> (star-visual-magnitude Algol)
2.66
> (star-spectral-type Algol)
b8

Check to make sure that it satisfies the type predicate.

> (star? Algol)
#t

Some implementations of Scheme extend the standard language to provide a much simpler syntax for defining record types, saving the programmer a lot of routine work by having the computer construct the definitions of the constructor, the accessors, and the type predicate. For a long time, however, different implementations of Scheme supported record types in radically different ways. Fortunately, in the last five years, implementers have begun to support and use the syntax illustrated by the following expression:

(define-record-type star
  (make-star name right-ascension declination visual-magnitude spectral-type)
  star?
  (name star-name)
  (right-ascension star-right-ascension)
  (declination star-declination)
  (visual-magnitude star-visual-magnitude)
  (spectral-type star-spectral-type))

The keyword define-record-type informs Scheme that we are giving it the information it needs to build all of the procedures for records of a new kind. The identifier immediately after define-record-type is the name of the new type -- in this case, star.

After that, the programmer writes out, between parentheses, the name of the constructor and the names of the fields that she wants a record of this type to contain. Here make-star is the name of the constructor and the remaining five identifiers are the field names.

After that, the programmer supplies an identifier for the type predicate; here that identifier is star?.

Finally, the programmer provides a sequence of accessor specifications. Each such specification is enclosed in parentheses and consists of two identifiers, one indicating which field is being accessed, the other the intended name for the accessor procedure for that field.

The define-record-type expression works out what the definitions of all these procedures should look like and processes all those definitions implicitly. After Scheme has seen the expression above, you can proceed at once to use those procedures, thus:

> (define Algol (make-star "Beta Persei" 3.13 40.93 2.66 'B8))
> Algol
#(struct:star "Beta Persei" 3.13 40.93 2.66 b8)
> (star-visual-magnitude Algol)
2.66
> (star-spectral-type Algol)
b8
> (star? Algol)
#t

Note that when DrScheme prints a star record, it looks a little different from the version that we saw when we were using lists to implement records, above.

The define-record-type-expressions are not supported directly in DrScheme, either, but the PLT folks have included a PLT Scheme source code file that you can use to add this new expression type to any of the versions of their language. You can find this file at /home/stone/courses/scheme/examples/record.ss, and you're welcome to copy it into your home directory; alternatively, you can download it from http://download.plt-scheme.org/scheme/plt-clean-cvs/collects/srfi/9/record.ss. Once you have your own copy, you can put the line

(require "record.ss")

at the top of any file that needs records, and DrScheme will find the file and set itself up to process define-record-type-expressions. (The require form itself presupposes that you are using one of the PLT languages, such as MzScheme or MrEd, within DrScheme; standard Scheme under the Revised5 report on the algorithmic language Scheme does not support require, either.)


Scheme Requests for Implementation

The fact that more and more implementers of Scheme are finding some way or other to support the define-record-type mechanism described above is not accidental. Shortly after the appearance of Revised5 report on the algorithmic language Scheme in 1998, the leading Scheme designers, implementers, and language theorists agreed to set up a Web site and some e-mail lists inteded specifically as a forum for proposing new features for Scheme, evaluating such proposals, and defining them carefully and uniformly enough to enable them to serve as the basis for future implementations of the language. Such proposals are called ``Scheme requests for implementation,'' or SRFIs (pronounced ``surfies'').

The site, http://srfi.schemers.org/, was established in November 1998, and now contains information about fifty-two proposed extensions of the language. Seven of them were flawed in various ways and were eventually withdrawn by their authors; nine are still being debated. Most of the remaining thirty-six have been widely implemented. The PLT folks, for instance, currently support twenty-four of them, which is a fairly typical rate of adoption.

Of course, nothing requires an implementer to pay any attention to SRFIs at all. However, the advantages of standardizing extensions to the language are obvious, and most implementers feel a kind of obligation to help out Scheme programmers by enabling them to write more portable Scheme programs.

One of the pleasant features of the SRFI Web site is that you can read all of the comments that people make about the proposals and get a sense of what programming-language designers think about and how they work. For instance, you might find it interesting to look at the discussion archive for the SRFI that proposes define-record-type-expressions; it is at http://srfi.schemers.org/srfi-9/mail-archive/maillist.html.


This document is available on the World Wide Web as

http://www.cs.grinnell.edu/~stone/courses/scheme/records.xhtml

Validated as XHTML 1.1 by the World Wide Web Consortium Cascading Style Sheet validated by the World Wide Web Consortium

created April 29, 1997
last revised March 10, 2004

John David Stone (stone@cs.grinnell.edu)