Title

ERR5RS Records

Author

William D Clinger

Status

This SRFI is currently in ``draft'' status. To see an explanation of each status that a SRFI can hold, see here. To provide input on this SRFI, please mail to <srfi minus 99 at srfi dot schemers dot org>. See instructions here to subscribe to the list. You can access previous messages via the archive of the mailing list.

Table of contents

Abstract

Many Scheme programmers have considered records to be one of the most important features missing from the R5RS. The R6RS proposed a record system, but its design has been widely criticized and it was not intended for use in R5RS programs anyway.

This SRFI proposes a better record system for use in R5RS, ERR5RS, and R6RS programs. The syntactic layer of this SRFI's record system is an extension of SRFI 9. The procedural and inspection layers of this SRFI's record system are perfectly compatible with its syntactic layer. This entire SRFI is compatible with the procedural and inspection layers of the R6RS record system, but offers several worthwhile improvements over the R6RS system.

Issues

This SRFI's proposed answers to the following questions are indicated within parentheses.

Rationale

In most programming languages, records (aka structures or classes) are important because they can package component values of different types into a single object.

Scheme's vectors and procedures provided that capability already, but records remained important for two reasons:

For many programmers, records were the most important new feature of the R6RS, but the specific record systems that were proposed by the R6RS have been widely criticized. Over 30% of those who voted against ratification mentioned the record systems as one of their reasons. [1]

The ERR5RS record system described by this SRFI provides a simpler and fully portable alternative to the R6RS record system. The ERR5RS record system consists of

The ERR5RS record system does not mandate support for the non-generative, sealed, and/or opaque features of the R6RS record system. Implementations of this SRFI may extend the ERR5RS record system to support those features, however, and this SRFI recommends an API to implementations that support those features. With those extensions, the ERR5RS record system has the same expressive power as the R6RS record system. Hence the record system described by this SRFI can serve as either or both of the following:

The following subsections develop the rationale for this SRFI by considering

Records Before R6RS

The importance of adding records to Scheme has been recognized for more than twenty years. [2] The basic idea behind the SRFI 9 and R6RS record systems was outlined by Norman Adams on 8 July 1987, following similar ideas that had been implemented in T and MIT CScheme. [3] Jonathan Rees posted a revision of Adams's proposal on 26 May 1988. [4] Pavel Curtis proposed an extension of Rees's proposal on 18 August 1989, noting that it had been approved by consensus at the first meeting of BASH (Bay Area Scheme Hackers?). [5] The rrrs-authors archive includes several responses to these proposals that are worth reading.

The Rees/Curtis proposal was revived in 1992. [6] When the RnRS authors met on 25 June 1992 in Palo Alto, they felt that this proposal needed more discussion. [7] Kent Dybvig objected to the proposal on several grounds, including the provision of inspection facilities, the inability to define immutable records, and the use of procedures instead of special forms. Although 9 authors favored adoption of the records proposal, 11 opposed it.

The topic of records was revived again on 23 April 1996 by Bruce Duba, Matthew Flatt, and Shriram Krishnamurthi. [8] Alan Bawden and Richard Kelsey observed that the Duba/Flatt/Krishnamurthi proposal was essentially the same as Pavel Curtis's, which Kelsey reposted. Kent Dybvig objected once again, on the same three grounds. He also argued that procedural interfaces are difficult to compile efficiently, and that this inefficiency would create portability problems. [9]

In reality, however, procedural interfaces add no inefficiency. It is now agreed that syntactic interfaces offer no advantages for generative records. [10] Even for non-generative records, the claimed inefficiency consists of a single load instruction, which optimizing compilers can eliminate---along with the entire runtime check that includes the load instruction---for all but the first of a sequence of operations that access the same record. (That optimization is a straightforward extension of the optimization that eliminates the pair check when computing the cdr of a list whose car has already been computed.) Furthermore, it turns out that even the occasional load instruction is no harder to remove using a procedural interface than when using a syntactic interface. [11] In R6RS library chapter 6, therefore, both of the statements that claim an advantage in efficiency for the syntactic layer have no basis in fact. (These two statements appear in the next-to-last paragraph before section 6.1, and in the note that follows the specification of parent-rtd.)

On 24 April 1996, Bill Rozas suggested the idea of having two separate APIs, one procedural and one syntactic, for the same record facility. [12] Two days later, Dybvig proposed a compromise along those lines [13] that incorporated several artificial restrictions, which were apparently motivated by concerns about the alleged extra load instruction. [14] Dybvig and Rozas continued to develop this proposal, and presented a summary of it following the 1998 Scheme Workshop. [15] I have been unable to locate a written or online copy of this proposal.

SRFI 9, submitted by Richard Kelsey in July 1999, is a syntactic API in the tradition of the Rees, Curtis, and Duba/Flatt/Krishnamurthi proposals. [16]

Single inheritance was added by Larceny in 1998, and by Chez Scheme in 1999. [17]

SRFI 57, submitted by Andre van Tonder in September 2004, features label polymorphism, which can be considered a form of structural subtyping and multiple inheritance. [18]

R6RS Records

The R6RS proposes a three-layer single inheritance system, with syntactic, procedural, and inspection layers. [19]

R6RS Records: procedural layer

The R6RS procedural layer generally requires at least three separate definitions for each level of inheritance: the record-type descriptor, at least one record-constructor descriptor, and an actual constructor (if instances of the record-type are to be created).

The (unratified) R6RS rationale [20] describes the constructor-descriptor mechanism as "an infrastructure for creating specialized constructors, rather than just creating default constructors that accept the initial values of all the fields as arguments. This infrastructure achieves full generality while leaving each level of an inheritance hierarchy in control over its own fields and allowing child record definitions to be abstracted away from the actual number and contents of parent fields." Neither the (ratified) R6RS library document nor the (unratified) R6RS rationale consider the fact that the constructor-descriptor mechanism adds unnecessary complexity to what is by far the most common case: record definitions that do not require specialized constructors. Neither document considers the fact that the benefits of the constructor-descriptor mechanism are small even when specialized constructors are needed, as in the first example below.

The R6RS library specification of records says that a record type is "specified by a record-type descriptor, which is an object that specifies the fields of the record and various other properties that all records of that type share." [21] Since the record-type descriptor is an object, it can be the value of a variable that is exported by a library. As discussed below, however, the R6RS syntactic layer uses a different notion of record type that libraries may not be able to export.

R6RS Records: syntactic layer

The R6RS syntactic layer consists of a define-record-type syntax that is incompatible with the syntaxes of the same name defined by SRFI 9 and SRFI 99 (this SRFI).

According to R6RS library section 6.2, an R6RS define-record-type form binds the record name "to an expand-time or run-time representation of the record type [that] can be used as parent name in syntactic record-type definitions that extend this definition. It can also be used as a handle to gain access to the underlying record-type descriptor and constructor descriptor".

Note that portable code cannot assume the record name is bound to a record-type descriptor. Portable code can only assume that the record name is bound to "an expand-time or run-time representation", whose semantics is not otherwise explained by the R6RS and R6RS library documents. In particular, portable code cannot assume that the record name can be exported by a library; libraries can export names that are bound to objects or to syntax, but the R6RS does not require the thing to which a record name is bound to be either of those things.

The mysterious entity to which a record name is bound can be used as a handle to recover a record-type descriptor or constructor descriptor by using the record-type-descriptor or record-constructor-descriptor syntaxes, respectively. The recovered record-type descriptor and constructor descriptor may be exported from a library, and that is apparently the only portable way for a library to export an R6RS record type that was defined using the R6RS syntactic layer.

The recovered record-type descriptor and constructor descriptor also provide a way for the procedural layer to define new record types that inherit from record types defined by the syntactic layer. Similarly, it is possible for the syntactic layer to use a parent-rtd clause to define new record types that inherit from record types defined by the procedural layer.

The two notions of record type that are used by the procedural and syntactic layers are not interchangeable, however. In either direction, defining a new record type that inherits from some previously defined record type requires the programmer to know whether the previously defined record type was defined using the procedural or the syntactic layer. If the procedural and syntactic layers of the R6RS were fully compatible, then changing a record type definition from procedural to syntactic (or vice versa) would be transparent to clients. As the R6RS record facility is defined, however, that minor change will break all code that inherits from the record type.

R6RS library chapter 6 attempts to excuse that incompatibility, and the interoperability and maintenance problems that result from it, on the basis of efficiency. Recall, however, that the claimed efficiency of the R6RS syntactic layer is illusory. In reality, the R6RS design offers no advantages over a simpler and more orthogonal design (such as the one specified by this SRFI) in which the syntactic and procedural layers both use the same notion of record type.

The problems described above were known and had been documented before the R6RS documents were put to a vote, but the R6RS documents were ratified anyway. [22] At this point, the best that can be done is to use the SRFI process to specify a better record facility, and to warn programmers of the problems they will encounter if they use the record facilities described within the R6RS library document.

Design Rationale for ERR5RS Records

The ERR5RS syntactic layer described by this SRFI is based upon the Rees/Curtis/Duba/Flatt/Krishnamurthi/Kelsey/SRFI-9 tradition, changing only a few details to improve interoperability with records defined by the ERR5RS and R6RS procedural layers.

The define-record-type syntax specified by this SRFI is compatible with and extends SRFI 9, which is one of the more widely accepted SRFIs. The extensions include single inheritance and (optional) implicit naming, along with succinct abbreviations for specifying whether a field is immutable or mutable.

The procedural layer specified by this SRFI is fully compatible with its define-record-type syntax. Both the procedural and syntactic layers can define new record types that inherit from previously defined record types without requiring programmers to know which layer was used to define the parent type.

In implementations of the R6RS, a SRFI 99 record type coincides with the R6RS notion of a record-type descriptor. Portable libraries can safely export SRFI 99 record types even if they were defined using the syntactic layer of SRFI 99.

In procedure names, SRFI 99 uses rtd as an abbreviation for record-type descriptor. This naming convention prevents name clashes between SRFI 99 and the R6RS procedural and inspection layers, which makes it easier for R6RS programs to import SRFI 99 libraries. R6RS programs must take care when importing the R6RS syntactic layer, however, because that library's exports conflict with both SRFI 9 and with SRFI 99.

When implemented properly, SRFI 99 records will be just as efficient as R6RS records. SRFI 99 is simpler than R6RS records, both in specification and in implementation. SRFI 99 is strictly less powerful than the R6RS records facility because SRFI 99 does not require implementations to provide sealed, opaque, or non-generative records. On the other hand, SRFI 99 describes three optional extensions (the sealed, opaque, and uid arguments to make-rtd) that would give SRFI 99 the same power as R6RS records. With those three extensions, SRFI 99 becomes a simple and efficient foundation for implementing R6RS records.

The record system described by this SRFI has been implemented in Larceny. It is the primary record system used by Larceny's implementation of the R6RS, including the (rnrs records syntactic) library. Larceny demonstrates both the efficiency of ERR5RS records and the ease of interoperability between SRFI 9, ERR5RS, and the procedural and inspection layers of R6RS records.

Specification

All implementations of SRFI 99 must provide the following libraries:

    (srfi :99)                       ; alias for (srfi :99 records)
    (srfi :99 records)               ; composite of the next three
    (srfi :99 records procedural)
    (srfi :99 records inspection)
    (srfi :99 records syntactic)

Implementations of ERR5RS should provide the following aliases as well:

    (err5rs records)                 ; alias for (srfi :99 records)
    (err5rs records procedural)      ; alias for (srfi :99 records procedural)
    (err5rs records inspection)      ; alias for (srfi :99 records inspection)
    (err5rs records syntactic)       ; alias for (srfi :99 records syntactic)

The specification also describes how Scheme's standard equivalence predicates behave with respect to records, and shows how some R6RS examples can be translated to use the ERR5RS libraries instead.

When the following specification says that a procedure is said to be equivalent to some R6RS procedure, the equivalence holds only when all arguments have the properties required of them by the R6RS specification. Neither ERR5RS nor this SRFI mandate the R6RS exception semantics for programs that violate the specification.

Procedural Layer

The (srfi :99 records procedural) library exports the following procedures.

(make-rtd name fieldspecs)

(make-rtd name fieldspecs parent)

name is a symbol, which matters only to the rtd-name procedure of the inspection layer. fieldspecs is a vector of field specifiers, where each field specifier is one of

The optional parent is an rtd or #f. It is an error for any of the symbols in fieldspecs to name more than one of the fields specified by fieldspecs, but the field names in fieldspecs may shadow field names in the parent record-type.

Implementations may wish to extend this procedure to support the non-generative, sealed, and/or opaque features of the R6RS. The recommended way to support those features is to allow any combination of the following arguments to follow the optional parent argument:

Returns an R6RS-compatible record-type descriptor. Could be defined (without the recommended error checking, and without the extensions described above) in terms of the R6RS procedural layer by

    (define (make-rtd name fieldspecs . rest)
      (make-record-type-descriptor
       name
       (if (null? rest) #f (car rest))
       #f #f #f
       (vector-map (lambda (fieldspec)
                     (if (symbol? fieldspec)
                         (list 'mutable fieldspec)
                         fieldspec))
                   fieldspecs)))

(rtd? obj)

Equivalent to the record-type-descriptor? procedure of the R6RS.

(rtd-constructor rtd)

(rtd-constructor rtd fieldspecs)

rtd is a record-type descriptor, and fieldspecs is an optional vector of symbols.

If no fieldspecs argument is supplied, then rtd-constructor returns a procedure that expects one argument for each field of the record-type described by rtd and returns an instance of that record-type with its fields initialized to the corresponding arguments. Arguments that correspond to the fields of the record-type's parent (if any) come first.

If fieldspecs is supplied, then rtd-constructor returns a procedure that expects one argument for each element of fieldspecs and returns an instance of the record-type described by rtd with the named fields initialized to the corresponding arguments.

It is an error if some symbol occurs more than once in fieldspecs. Fields of a derived record-type shadow fields of the same name in its parent; the fieldspecs argument cannot be used to initialize a shadowed field.

Note: The optional second argument was proposed by Pavel Curtis, and interoperates well with SRFI 9.

Could be defined in terms of the R6RS procedural layer and ERR5RS inspection layer by:

    (define (rtd-constructor rtd . rest)
    
      ; Computes permutation and allocates permutation buffer
      ; when the constructor is created, not when the constructor
      ; is called.  More error checking is recommended.

      (define (make-constructor fieldspecs allnames maker)
        (let* ((k (length fieldspecs))
               (n (length allnames))
               (buffer (make-vector n (unspecified)))
               (reverse-all-names (reverse allnames)))
    
          (define (position fieldname)
            (let ((names (memq fieldname reverse-all-names)))
              (assert names)
              (- (length names) 1)))
    
          (let ((indexes (map position fieldspecs)))
    
            ; The following can be made quite efficient by
            ; hand-coding it in some lower-level language,
            ; e.g. Larceny's mal.  Even case-lambda would
            ; be good enough in most systems.

            (lambda args
              (assert (= (length args) k))
              (for-each (lambda (arg posn)
                          (vector-set! buffer posn arg))
                        args indexes)
              (apply maker (vector->list buffer))))))
    
      (if (null? rest)
          (record-constructor
           (make-record-constructor-descriptor rtd #f #f))
          (begin (assert (null? (cdr rest)))
                 (make-constructor
                  (vector->list (car rest))
                  (vector->list (rtd-all-field-names rtd))
                  (record-constructor
                   (make-record-constructor-descriptor rtd #f #f))))))

(rtd-predicate rtd)

Equivalent to the record-predicate procedure of the R6RS.

(rtd-accessor rtd field)

field is a symbol that names a field of the record-type described by the record-type descriptor rtd. Returns a unary procedure that accepts instances of rtd (or any record-type that inherits from rtd) and returns the current value of the named field.

Fields in derived record-types shadow fields of the same name in a parent record-type.

(rtd-mutator rtd field)

field is a symbol that names a field of the record-type described by the record-type descriptor rtd. Returns a binary procedure that accepts instances of rtd (or any record-type that inherits from rtd) and a new value to be stored into the named field, performs that side effect, and returns an unspecified value.

Fields in derived record-types shadow fields of the same name in a parent record-type.


Inspection Layer

The (srfi :99 records inspection) library exports the following procedures.

(record? obj)

Equivalent to its R6RS namesake.

(record-rtd record)

Equivalent to its R6RS namesake.

(rtd-name rtd)

Equivalent to the record-type-name procedure of the R6RS.

(rtd-parent rtd)

Equivalent to the record-type-parent procedure of the R6RS.

(rtd-field-names rtd)

Equivalent to the record-type-field-names procedure of the R6RS. (That is, it returns a vector of the symbols that name the fields of the record-type represented by rtd, excluding the fields of parent record-types.)

(rtd-all-field-names rtd)

Returns a vector of the symbols that name the fields of the record-type represented by rtd, including the fields of its parent record-types, if any. The fields of parent record-types come before the fields of its children, with each subsequence in the same order as in the vectors that would be returned by calling rtd-field-names on rtd and on all its ancestral record-type descriptors.

Could be defined by

    (define (rtd-all-field-names rtd)
      (define (loop rtd othernames)
        (let ((parent (rtd-parent rtd))
              (names (append (vector->list
                              (rtd-field-names rtd))
                             othernames)))
          (if parent
              (loop parent names)
              (list->vector names))))
      (loop rtd '()))

(rtd-field-mutable? rtd field)

rtd is a record-type descriptor, and field is a symbol naming a field of the record-type described by rtd. Returns #t if the named field is mutable; otherwise returns #f.


Syntactic Layer

The syntactic layer consists of SRFI 9 extended with single inheritance and (optional) implicit naming.

All ERR5RS record-type definitions are generative, but ERR5RS drops the SRFI 9 restriction to top level, mainly because the R6RS allows generative definitions wherever a definition may appear.

The (srfi :99 records syntactic) library exports the define-record-type syntax specified below.

The syntax of an ERR5RS record-type definition is

 <definition>           
   -> <record type definition>           ; addition to 7.1.6 in R5RS

 <record type definition>
   -> (define-record-type <type spec>
        <constructor spec>
        <predicate spec>
        <field spec> ...)

 <type spec>  -> <type name>
              -> (<type name> <parent>)

 <constructor spec>
              -> #f
              -> #t
              -> <constructor name>
              -> (<constructor name> <field name> ...)

 <predicate spec>
              -> #f
              -> #t
              -> <predicate name>

 <field spec> -> <field name>
              -> (<field name>)
              -> (<field name> <accessor name>)
              -> (<field name> <accessor name> <mutator name>)

 <parent>           -> <expression>

 <type name>        -> <identifier>
 <constructor name> -> <identifier>
 <predicate name>   -> <identifier>
 <accessor name>    -> <identifier>
 <mutator name>     -> <identifier>
 <field name>       -> <identifier>

The semantics of a record type definition is the same as in SRFI 9: the record type definition macro-expands into a cluster of definitions that

An ERR5RS record type definition extends SRFI 9 with the following additional options:


Record Identity

Two ERR5RS records with fields are eqv? if and only if they were created by the same (dynamic) call to some record constructor. Two ERR5RS records are eq? if and only if they are eqv?.

Two ERR5RS records for which the record? predicate returns true are equal? if and only if they are eqv?. This SRFI does not specify the semantics of equal? on opaque records, which are not required by this SRFI. (The R6RS requires equal? and eqv? to behave the same on all records, unless the records happen to be opaque records that represent pairs, vectors, strings, or bytevectors.)

(Historical note: Pavel Curtis proposed that equal? behave the same as eqv?.)

A define-record-type form macro-expands into code that calls make-rtd each time the expanded record-type definition is executed. Two ERR5RS record-type descriptors are eqv? if and only if they were created by the same (dynamic) call to make-rtd.


Examples

R6RS library section 6.3 includes two extended examples that provide a nice comparison of the R6RS and ERR5RS record systems, especially since these two examples were designed to highlight the use of R6RS record-constructor descriptors in combination with inheritance.

Example 1

Using ERR5RS records, the first example becomes:

(define rtd1
  (make-rtd 'rtd1 '#((immutable x1) (immutable x2))))

(define rtd2
  (make-rtd 'rtd2 '#((immutable x3) (immutable x4)) rtd1))

(define rtd3
  (make-rtd 'rtd3 '#((immutable x5) (immutable x6)) rtd2))

(define protocol1
  (lambda (p)
    (lambda (a b c)
      (p (+ a b) (+ b c)))))

(define protocol2
  (lambda (n)
    (lambda (a b c d e f)
      (let ((p (n a b c)))
        (p (+ d e) (+ e f))))))

(define protocol3
  (lambda (n)
    (lambda (a b c d e f g h i)
      (let ((p (n a b c d e f)))
        (p (+ g h) (+ h i))))))

(define make-rtd1
  (protocol1 (rtd-constructor rtd1)))

(define make-rtd2
  (let ((maker2 (rtd-constructor rtd2)))
    (protocol2
     (protocol1
      (lambda (x1 x2)
        (lambda (x3 x4)
          (maker2 x1 x2 x3 x4)))))))

(define make-rtd3
  (let ((maker3 (rtd-constructor rtd3)))
    (protocol3
     (protocol2
      (protocol1
       (lambda (x1 x2)
         (lambda (x3 x4)
           (lambda (x5 x6)
             (maker3 x1 x2 x3 x4 x5 x6)))))))))

(make-rtd3 1 2 3 4 5 6 7 8 9)

; evaluates to a record whose fields contain
; 3 5 9 11 15 17

The purpose of the R6RS record-constructor descriptors is to automate the idiom shown in the definitions of make-rtd1, make-rtd2, and make-rtd3 above, and to provide an alternative to procedural abstraction when eliminating the duplication of code seen in make-point/abs and make-cpoint/abs below.

Example 2

The second example illustrates the shadowing of fields in a parent record-type by fields in a derived record-type. Using ERR5RS records, the second example becomes:

(define :point
  (make-rtd 'point '#((mutable x) (mutable y))))

(define make-point (rtd-constructor :point))

(define point? (rtd-predicate :point))
(define point-x (rtd-accessor :point 'x))
(define point-y (rtd-accessor :point 'y))
(define point-x-set! (rtd-mutator :point 'x))
(define point-y-set! (rtd-mutator :point 'y))

(define p1 (make-point 1 2))
(point? p1)                     => #t
(point-x p1)                    => 1
(point-y p1)                    => 2
(point-x-set! p1 5)
(point-x p1)                    => 5

(define :point2
  (make-rtd 'point2 '#((mutable x) (mutable y)) :point))

(define make-point2
  (rtd-constructor :point2))
(define point2? (rtd-predicate :point2))
(define point2-xx (rtd-accessor :point2 'x))
(define point2-yy (rtd-accessor :point2 'y))

(define p2 (make-point2 1 2 3 4))
(point? p2)                     => #t
(point-x p2)                    => 1
(point-y p2)                    => 2
(point2-xx p2)                  => 3
(point2-yy p2)                  => 4

(define make-point/abs
  (let ((maker (rtd-constructor :point)))
    (lambda (x y)
      (maker (abs x) (abs y)))))

(point-x (make-point/abs -1 -2)) => 1
(point-y (make-point/abs -1 -2)) => 2

(define :cpoint
  (make-rtd 'cpoint '#((mutable rgb)) :point))

(define make-cpoint
  (let ((maker (rtd-constructor :cpoint)))
    (lambda (x y c)
      (maker x y (color->rgb c)))))

(define make-cpoint/abs
  (let ((maker (rtd-constructor :cpoint)))
    (lambda (x y c)
      (maker (abs x) (abs y) (color->rgb c)))))

(define cpoint-rgb
  (rtd-accessor :cpoint 'rgb))

(define (color->rgb c)
  (cons 'rgb c))

(cpoint-rgb (make-cpoint -1 -3 'red))   => (rgb . red)
(point-x (make-cpoint -1 -3 'red))      => -1
(point-x (make-cpoint/abs -1 -3 'red))  => 1

Reference Implementation

To simplify maintenance, the reference implementation is provided separately.


References

  1. http://www.r6rs.org/ratification/results.html
  2. http://swiss.csail.mit.edu/ftpdir/scheme-mail/HTML/rrrs-1987/msg00135.html
  3. http://swiss.csail.mit.edu/ftpdir/scheme-mail/HTML/rrrs-1987/msg00131.html
  4. http://swiss.csail.mit.edu/ftpdir/scheme-mail/HTML/rrrs-1988/msg00155.html
  5. http://www-swiss.ai.mit.edu/ftpdir/scheme-mail/HTML/rrrs-1989/msg00147.html
  6. http://www.swiss.ai.mit.edu/ftpdir/scheme-mail/HTML/rrrs-1992/msg00036.html
  7. http://www.swiss.ai.mit.edu/ftpdir/scheme-mail/HTML/rrrs-1992/msg00199.html
  8. http://www.swiss.ai.mit.edu/ftpdir/scheme-mail/HTML/rrrs-1996/msg00086.html
  9. http://www.swiss.ai.mit.edu/ftpdir/scheme-mail/HTML/rrrs-1996/msg00101.html
  10. http://lists.r6rs.org/pipermail/r6rs-discuss/2007-July/003093.html
  11. http://lists.r6rs.org/pipermail/r6rs-discuss/2007-July/003092.html
  12. http://www.swiss.ai.mit.edu/ftpdir/scheme-mail/HTML/rrrs-1996/msg00103.html
  13. http://www.swiss.ai.mit.edu/ftpdir/scheme-mail/HTML/rrrs-1996/msg00115.html
  14. http://www.swiss.ai.mit.edu/ftpdir/scheme-mail/HTML/rrrs-1996/msg00124.html
  15. http://schemers.org/Events/Workshops/Sep1998/minutes
  16. http://srfi.schemers.org/srfi-9/srfi-9.html
  17. http://srfi.schemers.org/srfi-76/srfi-76.html
  18. http://srfi.schemers.org/srfi-57/srfi-57.html
  19. http://www.r6rs.org/
  20. http://www.r6rs.org/final/html/r6rs-rationale/r6rs-rationale.html
  21. http://www.r6rs.org/final/html/r6rs-lib/r6rs-lib.html
  22. http://www.r6rs.org/ratification/results.html

Acknowledgments

I am grateful to David Rush and Andre van Tonder for their comments and criticisms, as well as to all those mentioned by name in the Rationale. The reference implementation is adapted from Larceny v0.97.


Copyright

Copyright (C) William D Clinger 2008. All Rights Reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. REMEMBER, THERE IS NO SCHEME UNDERGROUND. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


Editor: David Van Horn

Valid XHTML 1.0!