Title

Records

Author

André van Tonder

Status

This SRFI is currently in ``draft'' status. To see an explanation of each status that a SRFI can hold, see here. It will remain in draft status until 2004/11/08, or as amended. To provide input on this SRFI, please mailto:srfi-57@srfi.schemers.org. See instructions here to subscribe to the list. You can access previous messages via the archive of the mailing list.

Abstract

We describe a syntax for defining record types. A predicate, constructor, and field accessors and modifiers may be specified for each record type. Records may be constructed positionally or by field labels. A mechanism for record subtyping is specified. A syntax is provided for both destructive and functional update, and for composing records.

Rationale

The existing SRFI-9 [1] provides a facility for defining record types. It is simple and clean, but does not support various common record operations and programming idioms particularly well. Such support was left to extensions such as the current SRFI, which provides For efficiency, the SRFI specification was designed with the constraint that all record expressions containing field labels be translatable into positional expressions at macro-expansion time.

Specification

Declaration

The syntax of a record type definition is as follows:
 <command or definition>           
   -> <record type definition>           ; addition to 7.1.6 in R5RS

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

 <type clause> -> <type name>                           
               -> (<type name> <supertype name> ...)  

 <constructor clause> -> (<constructor name> <field label> ...)               
                      -> <constructor name> 
                      -> #f

 <predicate clause> -> <predicate name>                 
                    -> #f

 <field clause> -> (<field label> <accessor clause> <modifier clause>) 
                -> (<field label> <accessor clause>)
            
 <accessor clause> -> <accessor name>                 
                   -> #f

 <modifier clause> -> <modifier name>                 
                   -> #f             

 <field label> -> <identifier>
 <... name>    -> <identifier>

An instance of define-record-type is equivalent to the following:

define-record-type is generative: each use creates a new record type that is distinct from all existing types, including other record types and Scheme's predefined types. Record-type definitions may only occur at top-level.

A note on subtyping and redefinition

In the following example, two supertypes define different accessors for the same field:
  (define-record-type foo make-foo foo? (x foo-x))
  (define-record-type bar make-bar bar? (x bar-x))
  (define-record-type (foo-bar foo bar) (x foo-bar-x))
Since any instance fb of foo-bar is an instance of both foo and bar, both foo-x and bar-x will work on fb, returning the x field. The accessor foo-bar-x will return the x field of any instances of foo-bar or further subtypes of foo-bar

In the following example, two record types define the same accessor:

  (define-record-type foo       make-foo foo? (x foo-x))
  (define-record-type (bar foo) make-bar bar? (x foo-x))
As in any define-... form, later bindings replace earlier bindings. After the second declaration is executed, foo-x will now work on instances of bar and not any longer on instances of the supertype foo that are not also instances of bar.

Examples

A simple record:

  (define-record-type point (make-point x y) point?
    (x get-x set-x!)           
    (y get-y set-y!))                    

  (define p (make-point 1 2))
  (get-y  p)                                 ==> 2
  (set-y! p 3))                              ==> (point (x 1) (y 3))                              
  (point? p)                                 ==> #t  
Note that the setter returns the updated record.

Optional elements:

Elements may be left out if not desired, as the following examples illustrate:
  
  (define-record-type node (make-node left right)) 
  (define-record-type leaf (make-leaf value))       
In these declarations, no predicates are bound. Also note that field labels listed in the constructor do not have to be repeated in the field clause list unless we want them to be mutable or we want to bind getters or setters.
              
  (define-record-type monday)               
  (define-record-type tuesday #f tuesday?)      
Here monday has no declared constructor or predicate, while tuesday has a predicate but no constructor.
  (define-record-type node make-node #f                                   
    (left  left  #f)                        
    (right right #f))                     
Here the constructor make-node has the default argument order and no predicate is bound. Fields are mutable, but no setters are bound. Also note that field labels are punned.

Subtyping:

  (define-record-type color make-color color?     ; make-color takes default argument order
    (hue hue set-hue!))                           ; punning of field labels is allowed

  (define-record-type (color-point color point)   ; more than one supertype
    (make-color-point x y hue)                   
    color-point?
    (info info))                                  ; field left undefined by constructor

  (define cp (make-color-point 1 2 'green))
  (color-point? cp)                          ==> #t
  (point? cp)                                ==> #t
  (color? cp)                                ==> #t
  (get-x  cp)                                ==> 1
  (hue    cp)                                ==> green
  (info   cp)                                ==> <undefined>

Labeled record expressions

The following syntax allows one to construct a record by labels. All the <field label>s have to belong to the record type <type name>. If this condition is not satisfied, an expansion time error must be signaled.
   <expression> -> (<type name> (<field label> <expression>) ...)

Rationale

The traditional practice of instantiating record values with a positional constructor procedure can lead to code that is hard to read and fragile under common operations such as adding, removing, or rearranging field declarations. The ability to populate record values by labels provides a more robust and readable alternative, especially useful when a record has more than two or three fields, or if it has supertypes. Field labels are checked for validity and the macro is compiled to a positional constructor at expansion time, thus eliminating a large class of potential programmer errors at no cost in efficiency.

Example

  (color-point (info 'hi) (x 1) (y 2))  
            
                 ==> (color-point (hue <undefined>) (x 1) (y 2) (info hi)) 

Record update

The following syntax allows different forms of record update:
   <expression> -> (record-update  <record> <type name> (<field label> <expression>) ...)
                -> (record-update* <record> <type name> (<field label> <expression>) ...)
                -> (record-update! <record> <type name> (<field label> <expression>) ...)
The first alternative is used for polymorphic functional record update. The expression <record> must evaluate to a record value that belongs to a subtype of <type name>. The result will be a new record value of the same type as the original <record>, with the given fields updated. The original record value is unaffected. All the <field label>s have to belong to the record type <type name>. If this condition is not satisfied, an expansion time error must be signaled.

The second alternative is used for monomorphic functional record update. The expression <record> must evaluate to a record value that belongs to a subtype of <type name>. The result will be a new record value of type <type name>, with the given fields updated. The original record value is unaffected. All the <field label>s have to belong to the record type <type name>. If this condition is not satisfied, an expansion time error must be signaled.

The third alternative is used for in-place record update. The expression <record> must evaluate to a record value that belongs to a subtype of <type name>. The result will be the original record value <type name>, with the given fields, which must be mutable, updated in place. Note that a useful value is returned. All the <field label>s have to belong to the record type <type name> and, in addition, be mutable. If this condition is not satisfied, an expansion time error must be signaled.

Rationale

In the presence of subtyping, polymorphic record update is not reducible to the other operations we have listed and therefore has to be provided as a built-in primitive [2].

Apart from this, a mechanism for functional update facilitates and encourages functional-style programming with records. It is particularly useful when we are updating only a few fields of a large record.

A rationale for the naming convention is as follows: the shortest name is given to the more general polymorphic update form, which is the safest in that it can be used in most instances where the monomorphic update* would suffice, and the former can indeed be replaced by the latter for efficiency when the programmer knows that the situation requires only monomorphic update. A destructive linear version update! is provided especially for cases where the programmer knows that no other references to a value exist, to produce what is, observationally, a pure-functional result. In these cases, an update or update* operation may be replaced by update! for efficiency. See SRFI-1 for a good discussion of linear update procedures.

Examples

  (define-record-type point2          #f #f (x) (y))
  (define-record-type (point3 point2) #f #f (x) (y) (z))
  
  (define p (point3 (x 1) (y 1) (z 3)))

  (record-update  p point2 (y 5))  ==> (point3 (x 1) (y 5) (z 3))  -- polymorphic update
  p                                ==> (point3 (x 1) (y 1) (z 3))  -- original unaffected
  (record-update* p point2 (y 5))  ==> (point2 (x 1) (y 5))        -- monomorphic update
  p                                ==> (point3 (x 1) (y 1) (z 3))  -- original unaffected
  (record-update! p point2 (y 5))  ==> (point3 (x 1) (y 5) (z 3))  -- destructive update
  p                                ==> (point3 (x 1) (y 5) (z 3))  -- original updated

Record composition

The following syntax provides a shorthand for composing record values:
   <expression> -> (record-compose ((<import-type name> <record>) ...)
                     (<export-type name> (<field label> <expression>) ...)
Here each expression <record> must evaluate to a record value belonging to a subtype of <import-type name>. The expression evaluates to a record value of type <export-type name> whose fields are populated as follows: All fields of the imported record values that also belong to the type <export type name> by comparing labels are imported from left to right, dropping any repeated fields. The additional fields <field label> are then populated with the corresponding <expression>, overwriting any fields with the same labels already imported. All the <field label>s have to belong to the record type <export type name>. If this condition is not satisfied, an expansion time error must be signaled.

The exported record type <export-type name> does not have to be a subtype of all the import types.

Rationale

Calculi for composing record values, such as the above scheme, may be used, for example, as "units" are used in PLT Scheme, or for writing what amounts to modules and functors in the sense of ML.

Examples

  (define c (color (hue 'green)))
  (define p (point (x 1) (y 2)))

  (record-compose ((point p)
                   (color c))
    (color-point (x 5)
                 (info 'hi)))  ==> (color-point (hue green) (x 5) (y 2) (info hi))

; Another record composition example:  

  (define-record-type monoid #f #f 
    (mult monoid.mult) 
    (one  monoid.one))
  
  (define-record-type abelian-group #f #f 
    (add  group.add) 
    (zero group.zero)
    (sub  group.sub))
  
  (define-record-type ring #f #f
    (mult ring.mult) 
    (one  ring.one)
    (add  ring.add) 
    (zero ring.zero)
    (sub  ring.sub))
  
  ; A simple functor

  (define (make-ring g m)
    (record-compose ((monoid        m)
                     (abelian-group g))
      (ring)))
  
  (define integer-monoid (monoid (mult *) 
                                 (one  1)))
  
  (define integer-group (abelian-group (add  +)
                                       (zero 0)
                                       (sub  -)))
  
  (define integer-ring (make-ring integer-group 
                                  integer-monoid))
  
  ((ring.add integer-ring) 1
                           2)   ;==> 3


Implementation

The reference implementations use the macro mechanism of R5RS. They do not use any other SRFI or any library.

Two reference implementations are given. One represents records as closures, while the other represents records as vectors. Depending on the application, the implementation based on closures may in fact perform better.

This version depends on define being treated as a binding form by syntax-rules. This is true for recent versions of portable syntax-case, for PLT, for Scheme48, and possibly others.

The SRFI specification was designed with the constraint that all record expressions containing field labels be translatable into positional expressions at macro-expansion time. For example, labeled record expressions and patterns should be just as efficient as positional constructors and patterns. This is true for the reference implementation.

In a deviation from the specification, record types defined by the reference implementation are not generative, and not disjoint from other types. Since various Schemes have their own ways of defining unique tags or types, it is left to implementors to choose the best way of achieving generativity. A simple mechanism for achieving generativity is implemented in SRFI-9. However, since this mechanism is not foolproof and slows down certain key primitives, it was not adopted here.

Only the names in the exports section should be visible to the user. The other name should be hidden by a suitable module system or naming convention.

Stub implementations are given for pattern matching, hopefully to be specified in a future SRFI. This is done because in the absence of static analysis, providing pattern matching as a primitive is much more efficient for certain common programming patterns. For example, writing

  (match r
    ((r1 : f1 ... fn) (.... (f1 r) ....
                       .... (f2 r) ....
                            ...
                       .... (fn r) ....)))

for the common pattern
  (cond
    ((r1? r) (.... (r1.field1 r) ....
              .... (r1.field2 r) ....
                   ...
              .... (r1.fieldn r) ....)))
will save at least n type tests, and up to kn type tests in the presence of subtyping with depth k.

The last section contains a few examples and (non-exhaustive) tests.

Reference implementation

Records as vectors

;==============================================================================

; Andre van Tonder, 2004.

; Records are implemented as vectors, with the attending virtual tables
; for polymorphism included in a dispatcher procedure.

;==============================================================================
; Exports:

(define-syntax define-record-type
  (syntax-rules ()
    ((define-record-type (name super ...) constructor-clause name? field ...)
     (build-record  name (super ...) constructor-clause name? (field ...)))
    ((define-record-type (name super ...) constructor-clause)
     (define-record-type (name super ...) constructor-clause #f))  
    ((define-record-type (name super ...))
     (define-record-type (name super ...) #f #f))
    ((define-record-type name . rest)
     (define-record-type (name) . rest))))

(define-syntax record-update
  (syntax-rules ()
    ((record-update record name (label exp) ...)
     (update name record ((label exp) ...)))))

(define-syntax record-update*
  (syntax-rules ()
    ((record-update* record name (label exp) ...)
     (record.labels name
      (update* name record ((label exp) ...))))))

(define-syntax record-update!
  (syntax-rules ()
    ((record-update! record name (label exp) ...)
     (update! name record ((label exp) ...)))))

(define-syntax record-compose
  (syntax-rules ()
    ((record-compose () (export-name (label exp) ...))
     (export-name (label exp) ...))
    ((record-compose ((name val) . imports) (export-name (label exp) ...))
     (syntax-map (name export-name) (record.labels)
      (compose 1 ((name val) . imports) (export-name (label exp) ...))))))

;==================================================================================
; Internal record utilities:

(define <record> '<record>)

(define <undefined> '<undefined>)

(define-syntax build-record      
  (syntax-rules ()
    ((build-record name supers (make-name pos-label ...) name? fields)
     (build-record name supers (make-name pos-label ...) (pos-label ...) name? fields))
    ((build-record name supers make-name name? fields)
     (build-record name supers make-name () name? fields))
    ((build-record name supers constructor (pos-label ...) name? fields)
     (get-immutable () fields                         ;  -> (immutables . stack) where stack = ()
      (get-mutable fields                             ;  -> (mutables immutables . stack)          
       (get-interfaces supers                         ;  -> (interfaces mutables immutables . stack)
        (union-mutables                               ;  -> (mut-labels interfaces mutables immutables . stack)
         (extract-labels fields (pos-label ...) name  ;  -> (labels mut-labels interfaces mutables immutables . stack)
          (union-labels                               ;  -> (labels interfaces mut-labels mutables immutables . stack)
           (recolor-identifiers
            (emit-record 1 name constructor name?)))))))))))

(define-syntax emit-record
  (syntax-rules () 
    ((emit-record (labels interfaces . stack) 1 name (make-name . pos-labels) name?) 
     (syntax-map (labels pos-labels interfaces) (add-temporaries) 
      (emit-record 2 labels name make-name name? . stack)))
    ((emit-record (labels . stack) 1 name make-name name?)
     (emit-record (labels . stack) 1 name (make-name . labels) name?))
    ((emit-record (((label index) ...) ((pos-label pos-temp) ...) (((super super-label ...) vtable) ...)) 2
                  labels name make-name name? (mut-label ...)
                  ((label** get-label** set-label!**) ...)
                  ((label*  get-label*) ...))
     (begin

       (define n 3)
       (begin 
         (define index n)
         (set! n (+ n 1)))
       ... 

       (define-syntax name                        
         (syntax-rules (label ...)
           ((name ("index") label) index)
           ...))
       
       (define maker 
         (let ((dispatcher 
                (let ((vtable (vector #f #f #f (record.index name super-label) ...))   
                      ...) 
                  (lambda (interface sk fk) 
                    (case interface              
                      ((name)  (sk (lambda (n) n)))
                      ((super) (sk (lambda (n) (vector-ref vtable n))))
                      ...
                      (else    (fk))))))
               (printer 
                (lambda (record)
                  (list 'name
                        (list 'label
                              (vector-ref record
                                          (record.index name label)))
                        ...))))             
           (lambda labels                               
             (make-record dispatcher
                          printer
                          label
                          ...))))
                
       (define (constructor pos-temp ...)
         (populate maker 1 labels (pos-label pos-temp) ...))
       
       (define-if make-name constructor)
       
       (define-if name?
         (lambda (val)
           (and (record? val)
                ((record.dispatcher val) 'name
                                         (lambda ignore #t)
                                         (lambda () #f)))))
                          
       (define-if get-label*
         (lambda (record)
           ((record.dispatcher record) 'name
                                       (lambda (idx)
                                         (vector-ref record
                                                     (idx (record.index name label*))))
                                       (lambda () (error "Invalid argument" (show record)
                                                         "for" 'get-label*)))))
       ...
       
       (define-if get-label**
         (lambda (record)
           ((record.dispatcher record) 'name
                                       (lambda (idx)
                                         (vector-ref record
                                                     (idx (record.index name label**))))
                                       (lambda () (error "Invalid argument" (show record)
                                                         "for" 'get-label**)))))
       ...
       
       
       (define-if set-label!**
         (lambda (record val)
           ((record.dispatcher record) 'name
                                       (lambda (idx)
                                         (vector-set! record
                                                      (idx (record.index name label**))
                                                      val)
                                         record)
                                       (lambda () (error "Invalid argument" (show record)
                                                         "for" 'set-label**)))))
       ...
       
       (define-syntax name
         (syntax-rules (: name label ... super ...)
           ((name ("labels") k)                   (syntax-apply k labels))
           ((name ("mutables") k)                 (syntax-apply k (mut-label ...)))
           ((name ("interfaces") k)               (syntax-apply k ((super super-label ...) ... (name label ...))))
           ((name ("match") val (: . pats) sk fk) (syntax-zip (pos-label ...) pats
                                                              (match-labels name labels val sk fk)
                                                              (syntax-error "Wrong number of patterns in match"
                                                                            (name : pats)
                                                                            "Positional fields are"
                                                                            (pos-label ...))))
           ((name ("match") val bindings sk fk)   (match-labels bindings name labels val sk fk))
           ((name ("mutable?") mut-label sk fk)   sk)
           ...
           ((name ("mutable?") other sk fk)       fk)
           ((name ("index") label)                index)
           ...
           ((name . bindings)                     (populate maker 1 labels . bindings))))
       
       (newline)
       (display "Record: ") (display 'name)  (newline)
       (display "Labels: ") (display 'labels) (newline)
       (display "Constr: ") (display '(pos-label ...)) (newline)
       (display "Supers: ")
       (for-each (lambda (int) (display int) (newline)
                   (display "        "))
                 (reverse '((super super-label ...) ...)))
       (newline)))))

(define-syntax make-record
  (syntax-rules ()
    ((make-record dispatcher printer field ...)
     (vector <record> dispatcher printer field ...))))

(define (record? x)
  (and (vector? x)
       (> (vector-length x) 2)             
       (eq? (vector-ref x 0) <record>)))

(define (record.dispatcher r) (vector-ref r 1))
(define (record.printer r)    (vector-ref r 2))

(define-syntax record.labels
  (syntax-rules ()
    ((record.labels name k)
     (name ("labels") k))))

(define-syntax record.mutables
  (syntax-rules ()
    ((record.mutables name k)
     (name ("mutables") k))))

(define-syntax record.if-mutable?
  (syntax-rules ()
    ((record.if-mutable? label name sk fk)
     (name ("mutable?") label sk fk))))

(define-syntax record.interfaces
  (syntax-rules ()
    ((record.interfaces name k)
     (name ("interfaces") k))))

(define-syntax record.index
  (syntax-rules ()
    ((record.index name label)
     (name ("index") label))))

(define (record-copy record)
  (let* ((ln  (vector-length record))
         (new (make-vector ln)))
    (let recur ((i 0))
      (if (= i ln)
          new
          (begin (vector-set! new i (vector-ref record i))
                 (recur (+ i 1)))))))

(define-syntax define-if
  (syntax-rules ()
    ((define-if #f   binding) (begin))
    ((define-if name binding) (define name binding))))

(define (record->list r)
  ((record.printer r) r))

(define (show x)
  (if (record? x)
      (record->list x)
      x))

(define-syntax extract-labels 
  (syntax-rules ()
    ((extract-labels stack fields pos-labels name k)
     (syntax-map fields (syntax-car)
       (syntax-append-after pos-labels
         (remove-duplicates top:if-free=
            (push-result stack k)))))))

(define-syntax get-immutable
  (syntax-rules ()
    ((get-immutable stack fields k)
     (syntax-filter fields (if-immutable?)
       (push-result stack k)))))
  
(define-syntax if-immutable?
  (syntax-rules ()
    ((if-immutable? (a b) sk fk) sk)
    ((if-immutable? other sk fk) fk)))

(define-syntax get-mutable
  (syntax-rules ()
    ((get-mutable stack fields k)
     (syntax-filter fields (if-mutable?)
       (push-result stack k)))))

(define-syntax if-mutable?
  (syntax-rules ()
    ((if-mutable? (a b c) sk fk) sk)
    ((if-mutable? other sk fk)   fk)))

(define-syntax get-interfaces
  (syntax-rules ()
    ((get-interfaces stack supers k)
     (syntax-map supers (record.interfaces)
      (syntax-append-all
       (remove-duplicates interface=
        (push-result stack k)))))))

(define-syntax interface=
  (syntax-rules ()
    ((interface= (name . stuff) (name* . stuff*) sk fk)
     (top:if-free= name name* sk fk))))

(define-syntax union-mutables
  (syntax-rules ()
    ((union-mutables (interfaces ((mutable . stuff) ...) . stack) k)
     (syntax-map interfaces (syntax-car)    
      (syntax-map (record.mutables)
       (syntax-cons-after (mutable ...)
        (syntax-append-all
         (remove-duplicates top:if-free=
          (push-result (interfaces ((mutable . stuff) ...) . stack) k)))))))))

(define-syntax union-labels
  (syntax-rules ()
    ((union-labels (labels mut-labels interfaces . stack) k)
     (syntax-map interfaces (syntax-cdr)
      (syntax-append (labels)
       (syntax-append-all
        (remove-duplicates top:if-free=
         (push-result (interfaces mut-labels . stack) k))))))))

(define-syntax recolor-identifiers
  (syntax-rules ()
    ((recolor-identifiers (labels . rest) k)
     (recolor top:if-free= labels rest 
      (syntax-cons-after labels k)))))
       
(define-syntax populate
  (syntax-rules ()
    ((populate maker 1 labels (label exp) ...)
     (order labels ((label . exp) ...) <undefined>
       (populate 2 maker)))
    ((populate ((label . exp) ...) 2 maker)
     (maker exp ...))))

(define-syntax order
  (syntax-rules ()
    ((order ordering alist default k)
     (order ordering alist alist () default k))
    ((order () () () accum default k)
     (syntax-apply k accum))
    ((order (label* . labels*) bindings () (binding* ...) default k)         
     (order labels* bindings bindings (binding* ... (label* . default)) default k))
    ((order () ((label . value) . rest) countdown bindings* default k)
     (syntax-error "Illegal label in" (label value)
                   "Legal bindings are" bindings*))
    ((order (label* . labels*) 
            ((label . value) binding ...) 
            (countdown . countdowns) 
            (binding* ...) 
            default 
            k)
     (if-free= label label*
               (order labels* 
                      (binding ...) 
                      (binding ...) 
                      (binding* ... (label . value)) 
                      default 
                      k)
               (order (label* . labels*) 
                      (binding ... (label . value))  
                      countdowns 
                      (binding* ...) 
                      default 
                      k)))))

(define-syntax update                      
  (syntax-rules ()
    ((update name record ((label exp) ...))
     (let* ((val record)
            (new (record-copy val)))
       ((record.dispatcher val) 'name 
                                (lambda (idx)
                                  (vector-set! new
                                               (idx (record.index name label))
                                               exp)
                                  ...
                                  new)
                                (lambda () (error "Value" (show val) 
                                                  "is not of record type" 'name)))))))

(define-syntax update!                         
  (syntax-rules ()
    ((update! name record ((label exp) ...))
     (syntax-andmap (label ...) (record.if-mutable? name)              
                    (let ((val record))
                      ((record.dispatcher val) 'name
                                               (lambda (idx) (vector-set! val
                                                                          (idx (record.index name label))
                                                                          exp)
                                                 ...
                                                 val)
                                               (lambda () (error "Value" (show val) 
                                                                 "is not of record type" 'name))))
                    (syntax-error "Attempt to update! immutable field:"
                                  (update! name record ((label exp) ...)))))))
       
(define-syntax update*
  (syntax-rules () 
    ((update* (label ...) name record ((label* exp) ...))
     (let ((val record))
       ((record.dispatcher val) 'name
                                (lambda (idx)
                                  (let ((new (name (label (vector-ref val
                                                                      (idx (record.index name label))))
                                                   ...)))
                                    (vector-set! new
                                                 (idx (record.index name label*))
                                                 exp)
                                    ...
                                    new))
                                (lambda () (error "Value" (show val) 
                                                  "is not of record type" 'name)))))))

(define-syntax compose     
  (syntax-rules ()
    ((compose (labels export-labels) 1 ((name record) . imports) (export-name . bindings))
     (syntax-filter labels (if-member? export-labels if-free=)
      (syntax-filter (if-not-member? bindings)
       (compose 2 ((name record) . imports) (export-name . bindings)))))
    ((compose (label ...) 2 ((name record) . imports) (export-name . bindings))
     (let ((val record))
       ((record.dispatcher val) 'name
                                (lambda (idx)
                                  (record-compose imports 
                                                  (export-name (label (vector-ref val
                                                                                  (idx (record.index name label))))
                                                               ...
                                                               . bindings)))
                                (lambda () (error "Value" (show val) 
                                                  "is not of record type" 'name)))))))
     
(define-syntax if-not-member?
  (syntax-rules ()
    ((if-not-member? label () sk fk) sk)
    ((if-not-member? label ((label* . stuff*) . bindings) sk fk)
     (if-free= label label*
               fk
               (if-not-member? label bindings sk fk)))))

(define-syntax record-match
  (syntax-rules (_)
    ((record-match exp)
     (error "Match failure for" exp))
    ((record-match (f . args) clause ...)
     (let ((val (f . args)))
       (record-match val clause ...)))
    ((record-match val ((name . pattern) . template) clause ...)
     (name ("match") val pattern 
           (begin . template) 
           (record-match val clause ...)))
    ((record-match val (_ . template) clause ...)
     (begin . template))
    ((record-match val (x . template) clause ...)
     (let ((x val)) . template))))

(define-syntax match-labels
  (syntax-rules ()
    ((match-labels ((label pat) ...) name labels val sk fk)
     (syntax-andmap (label ...) (if-member? labels if-free=)
                    (let ((fail (lambda () fk)))
                      (if (record? val)
                          ((record.dispatcher val) 'name
                                                   (lambda (idx)
                                                     (match-each ((pat (vector-ref val
                                                                                   (idx (record.index name label))))
                                                                  ...) 
                                                                 sk 
                                                                 fail))
                                                   fail)
                          (fail)))
                    (syntax-error "Attempt to match illegal label in match expression"
                                  (name (label pat) ...)
                                  "Legal labels are" labels)))))
                                  
     

(define-syntax match-each
  (syntax-rules ()
    ((match-each () sk fail)
     sk)
    ((match-each ((pat var) . bindings) sk fail)
     (record-match var
       (pat (match-each bindings sk fail))
       (_   (fail))))))


;====================================================================
; Internal syntax utilities:

(define-syntax syntax-error (syntax-rules ()))

(define-syntax syntax-apply
  (syntax-rules ()
    ((syntax-apply (f . args) exp ...) 
     (f exp ... . args))))

(define-syntax if-free=
  (syntax-rules ()
    ((if-free= x y kt kf)
      (let-syntax
          ((test (syntax-rules (x)
                   ((test x kt* kf*) kt*)
                   ((test z kt* kf*) kf*))))
        (test y kt kf)))))

(define-syntax top:if-free=
  (syntax-rules ()
    ((top:if-free= x y kt kf)
     (begin
       (define-syntax if-free=:test
         (syntax-rules (x)
           ((if-free=:test x kt* kf*) kt*)
           ((if-free=:test z kt* kf*) kf*)))
       (if-free=:test y kt kf)))))

(define-syntax syntax-cons
  (syntax-rules ()
    ((syntax-cons x rest k) (syntax-apply k (x . rest)))))

(define-syntax syntax-cons-after
  (syntax-rules ()
    ((syntax-cons-after rest x k) (syntax-apply k (x . rest)))))

(define-syntax syntax-car
  (syntax-rules ()
    ((syntax-car (h . t) k) (syntax-apply k h))))

(define-syntax syntax-cdr
  (syntax-rules ()
    ((syntax-cdr (h . t) k) (syntax-apply k t))))

(define-syntax syntax-foldr
  (syntax-rules ()
    ((syntax-foldr accum (f arg ...) () k)
     (syntax-apply k accum))
    ((syntax-foldr accum (f arg ...) (h . t) k)
     (syntax-foldr accum (f arg ...) t
       (f h arg ... k)))))

(define-syntax syntax-append
  (syntax-rules ()
    ((syntax-append (a ...) (b ...) k) (syntax-apply k (a ... b ...)))))

(define-syntax syntax-append-all
  (syntax-rules ()
    ((syntax-append-all lists k) 
     (syntax-foldr () (syntax-append-after) lists k))))

(define-syntax syntax-append-after
  (syntax-rules ()
    ((syntax-append-after y x k) (syntax-append x y k))))

(define-syntax syntax-filter
  (syntax-rules ()
    ((syntax-filter () (if-p? arg ...) k)
     (syntax-apply k ()))
    ((syntax-filter (h . t) (if-p? arg ...) k)
     (if-p? h arg ...
            (syntax-filter t (if-p? arg ...) (syntax-cons-after h k))
            (syntax-filter t (if-p? arg ...) k)))))

(define-syntax syntax-map
  (syntax-rules ()
    ((syntax-map () (f arg ...) k)      (syntax-apply k ()))
    ((syntax-map (h . t) (f arg ...) k) (syntax-map t (f arg ...)
                                          (syntax-map (f arg ...) h k)))
    ((syntax-map done (f arg ...) h k)  (f h arg ...
                                          (syntax-cons done k)))))

(define-syntax syntax-andmap
  (syntax-rules ()
    ((syntax-andmap () (if-p? arg ...) sk fk) sk)
    ((syntax-andmap (h . t) (if-p? arg ...) sk fk)
     (if-p? h arg ...
            (syntax-andmap t (if-p? arg ...) sk fk)
            fk))))
     
(define-syntax add-temporaries   
  (syntax-rules () 
    ((add-temporaries lst k)                (add-temporaries lst () k))
    ((add-temporaries () lst-temps k)       (syntax-apply k lst-temps))
    ((add-temporaries (h . t) (done ...) k) (add-temporaries t (done ... (h temp)) k)))) 

(define-syntax syntax-zip
  (syntax-rules ()
    ((syntax-zip () () sk fk)             (syntax-apply sk ()))
    ((syntax-zip () lst* sk fk)           fk)
    ((syntax-zip lst () sk fk)            fk)
    ((syntax-zip (h . t) (h* . t*) sk fk) (syntax-zip t t* 
                                                      (syntax-cons-after (h h*) sk) 
                                                      fk))))  
                                                                                               
(define-syntax push-result
  (syntax-rules ()
    ((push-result x stack k) (syntax-apply k (x . stack)))))

(define-syntax remove-duplicates
  (syntax-rules ()
    ((remove-duplicates lst compare? k)
     (remove-duplicates lst () compare? k))
    ((remove-duplicates () done compare? k)
     (syntax-apply k done))
    ((remove-duplicates (h . t) (d ...) compare? k)
     (if-member? h (d ...) compare? 
                 (remove-duplicates t (d ...) compare? k)
                 (remove-duplicates t (d ... h) compare? k)))))

(define-syntax if-member?
  (syntax-rules ()
    ((if-member? x () compare? sk fk) 
     fk)
    ((if-member? x (h . t) compare? sk fk)
     (compare? x h
               sk
               (if-member? x t compare? sk fk)))))

(define-syntax recolor
  (syntax-rules ()
    ((recolor compare labels () k)
     (syntax-apply k ()))
    ((recolor compare labels (h . t) k)
     (recolor compare labels h 
      (recolor compare labels t "combine" k)))
    ((recolor h-done compare labels to-do "combine" k)
     (recolor compare labels to-do
      (syntax-cons-after h-done k)))
    ((recolor compare labels id k)
     (get-equiv compare id labels k))))

(define-syntax get-equiv
  (syntax-rules ()
    ((get-equiv compare label () k)
     (syntax-apply k label))
    ((get-equiv compare label (h . t) k)
     (compare h label
              (syntax-apply k h)
              (get-equiv compare label t k)))))


;=============================================================================
; End of reference implementation
;=============================================================================

Records as closures

;==============================================================================

; Andre van Tonder, 2004. 

; Records are implemented as closures that inject their fields into
; a continuation.  

;==============================================================================
; Exports:

(define-syntax define-record-type
  (syntax-rules ()
    ((define-record-type (name super ...) constructor-clause name? field ...)
     (build-record  name (super ...) constructor-clause name? (field ...)))
    ((define-record-type (name super ...) constructor-clause)
     (define-record-type (name super ...) constructor-clause #f))  
    ((define-record-type (name super ...))
     (define-record-type (name super ...) #f #f))
    ((define-record-type name . rest)
     (define-record-type (name) . rest))))

(define-syntax record-update
  (syntax-rules ()
    ((record-update record name (label exp) ...)
     (record.labels name
      (update 1 name record ((label exp) ...))))))

(define-syntax record-update*
  (syntax-rules ()
    ((record-update* record name (label exp) ...)
     (record.labels name
      (update* 1 name record ((label exp) ...))))))

(define-syntax record-update!
  (syntax-rules ()
    ((record-update! record name (label exp) ...)
     (record.labels name
      (update! 1 name record ((label exp) ...))))))

(define-syntax record-compose
  (syntax-rules ()
    ((record-compose () (export-name (label exp) ...))
     (export-name (label exp) ...))
    ((record-compose ((name val) . imports) (export-name (label exp) ...))
     (syntax-map (name export-name) (record.labels)
      (compose 1 (export-name (label exp) ...) name val imports)))))


;==================================================================================
; Internal record utilities:

(define <record> '<record>)

(define <undefined> '<undefined>)

(define-syntax build-record      
  (syntax-rules ()
    ((build-record name supers (make-name pos-label ...) name? fields)
     (build-record name supers (make-name pos-label ...) (pos-label ...) name? fields))
    ((build-record name supers make-name name? fields)
     (build-record name supers make-name () name? fields))
    ((build-record name supers constructor (pos-label ...) name? fields)
     (get-immutables () fields                        ;  -> (immutables . stack) where stack = ()
      (get-mutables fields                            ;  -> (mutables immutables . stack)          
       (get-interfaces supers                         ;  -> (interfaces mutables immutables . stack)
        (union-mutables                               ;  -> (mut-labels interfaces mutables immutables . stack)
         (extract-labels fields (pos-label ...) name  ;  -> (labels mut-labels interfaces mutables immutables . stack)
          (union-labels                               ;  -> (labels mut-labels interfaces mutables immutables . stack)
           (recolor-identifiers
            (emit-record 1 name constructor name?)))))))))))

(define-syntax emit-record
  (syntax-rules () 
    ((emit-record (labels . stack) 1 name (make-name pos-label ...) name?) 
     (add-temporaries (pos-label ...)
      (emit-record 2 labels labels name make-name name? stack)))
    ((emit-record (labels . stack) 1 name make-name name?)
     (emit-record (labels . stack) 1 name (make-name . labels) name?))
    ((emit-record ((pos-label pos-temp) ...) 2 (label ...) labels
                  name make-name name? ((mut-label ...)
                                        ((super   super-label ...) ...)
                                        ((label** get-label** set-label!**) ...)
                                        ((label*  get-label*) ...) . stack))
     (begin
       
       (define maker 
         (let ()
           (define (printer record)
             ((cadr record) 'name  
                            (lambda labels
                              (let ((mut-label (unbox mut-label))
                                    ...)
                                (list 'name (list 'label label) ...)))
                            (lambda () (error))))
           
           (define (updater record)    
             ((cadr record) 'name
                            (lambda labels
                              (lambda (super-tag fields-k)         
                                (let ((mut-label (unbox mut-label))
                                      ...)
                                  (fields-k
                                   (case super-tag 
                                     ((name)  (lambda labels
                                                (maker . labels)))
                                     ((super) (lambda (super-label ...) 
                                                (maker . labels)))
                                     ...)))))
                            (lambda () (error))))
           
           (lambda labels
             (let ((mut-label (make-box mut-label))
                   ...)
               (list <record>                                            
                     (lambda (interface sk fk)           
                       (case interface               
                         ((name)  (sk label ...))
                         ((super) (sk super-label ...))
                         ...
                         (else (fk))))
                     updater
                     printer)))))
                
       (define (constructor pos-temp ...)
         (make-record maker labels (pos-label pos-temp) ...))
       
       (define-if make-name constructor)
       
       (define-if name?
         (lambda (val)
           (and (record? val)
                ((cadr val) 'name 
                            (lambda ignore #t)
                            (lambda () #f)))))
                          
       (define-if get-label**
         (lambda (record)
           ((cadr record) 'name
                          (lambda labels
                            (unbox label**)) 
                          (lambda () (error "Invalid argument" (show record)
                                            "for" 'get-label**)))))
       ...
       
       (define-if get-label*  
         (lambda (record)
           ((cadr record) 'name
                          (lambda labels label*)
                          (lambda () (error "Invalid argument" (show record)
                                            "for" 'get-label*))))) 
       ...
       
       (define-if set-label!**
         (lambda (record val)
           ((cadr record) 'name
                          (lambda labels
                            (begin (set-box! label** val)
                                   record))                  
                          (lambda () (error "Invalid argument" (show record)
                                            "for" 'set-label!**)))))
       ...
       
       (define-syntax name
         (syntax-rules (: name label ... super ...)
           ((name ("labels") k)                   (syntax-apply k labels))
           ((name ("positionals") k)              (syntax-apply k (pos-label ...)))
           ((name ("mutables") k)                 (syntax-apply k (mut-label ...)))
           ((name ("interfaces") k)               (syntax-apply k ((super super-label ...) ... (name label ...))))
           ((name ("mutable?") mut-label sk fk)   sk)
           ...
           ((name ("mutable?") other sk fk)       fk)
           ((name ("match") val (: . pats) sk fk) (syntax-zip (pos-label ...) pats
                                                              (match-labels 1 labels name val sk fk)
                                                              (syntax-error "Wrong number of patterns in match"
                                                                            (name : pats)
                                                                            "Positional fields are"
                                                                            (pos-label ...))))
           ((name ("match") val bindings sk fk)   (match-labels bindings 1 labels name val sk fk))
           ((name . bindings)                     (make-record maker labels . bindings))))
       
       (newline)
       (display "Record: ") (display 'name)  (newline)
       (display "Labels: ") (display 'labels) (newline)
       (display "Constr: ") (display '(pos-label ...)) (newline)
       (display "Supers: ")
       (for-each (lambda (int) (display int) (newline)
                   (display "        "))
                 (reverse '((super super-label ...) ...)))
       (newline)))))

(define-syntax record.labels
  (syntax-rules ()
    ((record-labels name k) (name ("labels") k))))

(define-syntax record.mutables
  (syntax-rules ()
    ((record.mutables name k) (name ("mutables") k))))

(define-syntax record.interfaces
  (syntax-rules ()
    ((record.interfaces name k) (name ("interfaces") k))))

(define-syntax define-if
  (syntax-rules ()
    ((define-if #f   binding) (begin))
    ((define-if name binding) (define name binding))))

(define (record? x)
  (and (pair? x)
       (eq? (car x) <record>)))

(define (record->list r)
  ((cadddr r) r))

(define (show x)
  (if (record? x)
      (record->list x)
      x))

(define-syntax extract-labels 
  (syntax-rules ()
    ((extract-labels stack fields pos-labels name k)
     (syntax-map fields (syntax-car)
       (syntax-append-after pos-labels
         (remove-duplicates top:if-free=
            (push-result stack k)))))))

(define-syntax get-immutables
  (syntax-rules ()
    ((get-immutables stack fields k)
     (syntax-filter fields (if-immutable?)
       (push-result stack k)))))
  
(define-syntax if-immutable?
  (syntax-rules ()
    ((if-immutable? (a b) sk fk) sk)
    ((if-immutable? other sk fk) fk)))

(define-syntax get-mutables
  (syntax-rules ()
    ((get-mutables stack fields k)
     (syntax-filter fields (if-mutable?)
       (push-result stack k)))))

(define-syntax if-mutable?
  (syntax-rules ()
    ((if-mutable? (a b c) sk fk) sk)
    ((if-mutable? other sk fk)   fk)))

(define-syntax get-interfaces
  (syntax-rules ()
    ((get-interfaces stack supers k)
     (syntax-map supers (record.interfaces)
      (syntax-append-all
       (remove-duplicates interface=
        (push-result stack k)))))))

(define-syntax interface=
  (syntax-rules ()
    ((interface= (name . stuff) (name* . stuff*) sk fk)
     (top:if-free= name name* sk fk))))

(define-syntax union-mutables
  (syntax-rules ()
    ((union-mutables (interfaces ((mutable . stuff) ...) . stack) k)
     (syntax-map interfaces (syntax-car)    ; abstract this
      (syntax-map (record.mutables)
       (syntax-cons-after (mutable ...)
        (syntax-append-all
         (remove-duplicates top:if-free=
          (push-result (interfaces ((mutable . stuff) ...) . stack) k)))))))))

(define-syntax union-labels
  (syntax-rules ()
    ((union-labels (labels mut-labels interfaces . stack) k)
     (syntax-map interfaces (syntax-cdr)
      (syntax-append (labels)
       (syntax-append-all
        (remove-duplicates top:if-free=
         (push-result (mut-labels interfaces . stack) k))))))))

(define-syntax recolor-identifiers
  (syntax-rules ()
    ((recolor-identifiers (labels . rest) k)
     (recolor top:if-free= labels rest 
      (syntax-cons-after labels k)))))
       
(define-syntax make-record
  (syntax-rules ()
    ((make-record maker labels (label exp) ...)
     (order labels ((label . exp) ...) <undefined>
       (populate maker)))))

(define-syntax populate
  (syntax-rules ()
    ((populate ((label . exp) ...) maker)
     (maker exp ...))))

(define-syntax order
  (syntax-rules ()
    ((order ordering alist default k)
     (order ordering alist alist () default k))
    ((order () () () accum default k)
     (syntax-apply k accum))
    ((order (label* . labels*) bindings () (binding* ...) default k)         
     (order labels* bindings bindings (binding* ... (label* . default)) default k))
    ((order () ((label . value) . rest) countdown bindings* default k)
     (syntax-error "Illegal label in" (label value)
                   "Legal bindings are" bindings*))
    ((order (label* . labels*) 
            ((label . value) binding ...) 
            (countdown . countdowns) 
            (binding* ...) 
            default 
            k)
     (if-free= label label*
               (order labels* 
                      (binding ...) 
                      (binding ...) 
                      (binding* ... (label . value)) 
                      default 
                      k)
               (order (label* . labels*) 
                      (binding ... (label . value))  
                      countdowns 
                      (binding* ...) 
                      default 
                      k)))))

(define-syntax update
  (syntax-rules ()
   ((update labels 1 name record bindings)
    (order labels bindings (#f)
     (syntax-map (insert-pattern)
      (update 2 record name))))
    ((update ((label pat binding) ...) 2 record name)
     (let ((val record))
       (record-match val 
         ((name (label pat) ...) 
          (((caddr val) val) 'name 
                             (lambda (k) (k binding ...)))))))))
    
(define-syntax update*
  (syntax-rules () 
    ((update* labels 1 name record bindings)
     (order labels bindings (#f)
      (syntax-map (insert-pattern)
       (update* 2 name record))))
    ((update* ((label pat binding) ...) 2 name record)
     (record-match record 
       ((name (label pat) ...)
        (name (label binding) ...))))))

(define-syntax insert-pattern
  (syntax-rules ()
    ((insert-pattern (label #f) k)      (syntax-apply k (label temp temp)))
    ((insert-pattern (label binding) k) (syntax-apply k (label _ binding)))))
            
(define-syntax update!
  (syntax-rules ()
    ((update! labels 1 name record ((label binding) ...))
     (recolor if-free= labels (label ...)
      (update! 2 labels name record (binding ...))))
    ((update! (label ...) 2 labels name record (binding ...))
     (let ((val record))
       ((cadr val) 'name
                   (lambda labels
                     (set-box! label binding)
                     ...
                     val)
                   (lambda () (error "Update!" (show val) 
                                     "is not of expected type" 'name)))))))

(define-syntax compose
  (syntax-rules () 
    ((compose (labels export-labels) 1 export name exp imports) 
     (syntax-filter labels (if-member? export-labels if-free=)
      (add-temporaries
       (compose 2 export name exp imports))))
    ((compose ((label temp) ...) 2 (export-name . bindings) name exp imports)
     (syntax-filter ((label temp) ...) (if-not-member? bindings)
      (compose 3 ((label temp) ...) export-name bindings name exp imports)))
    ((compose ((label* temp*) ...) 3 ((label temp) ...) export-name bindings name exp imports)
     (let ((val exp))
       (record-match val 
         ((name (label temp) ...)
          (record-compose imports (export-name (label* temp*) ... . bindings))))))))

(define-syntax if-not-member?
  (syntax-rules ()
    ((if-not-member? (label . stuff) () sk fk) sk)
    ((if-not-member? (label . stuff) ((label* . stuff*) . rest) sk fk)
     (if-free= label label*
               fk
               (if-not-member? (label . stuff) rest sk fk)))))

(define-syntax record-match
  (syntax-rules (_)
    ((record-match exp)
     (error "Match failure for" exp))
    ((record-match (f . args) clause ...)
     (let ((val (f . args)))
       (record-match val clause ...)))
    ((record-match val ((name . pattern) . template) clause ...)
     (name ("match") val pattern 
           (begin . template) 
           (record-match val clause ...)))
    ((record-match val (_ . template) clause ...)
     (begin . template))
    ((record-match val (x . template) clause ...)
     (let ((x val)) . template))))

(define-syntax match-labels
  (syntax-rules ()
    ((match-labels bindings 1 labels name val sk fk)
     (order labels bindings (_)
      (add-temporaries 
       (match-labels 2 val name sk fk))))
    ((match-labels (((label pat) var) ...) 2 val name sk fk)
     (syntax-filter ((label var) ...) (if-mutable-entry? name)
      (match-labels 3 ((label pat var) ...) val name sk fk)))
    ((match-labels ((mut-label mut-var) ...) 3 ((label pat var) ...) val name sk fk)
     (let ((fail (lambda () fk)))
       (if (record? val)
           ((cadr val) 'name
                       (lambda (var ...)
                         (let ((mut-var (unbox mut-var)) 
                               ...)
                           (match-each ((pat var) ...) sk fail)))
                       fail)
           (fail))))))

(define-syntax match-each
  (syntax-rules ()
    ((match-each () sk fail)
     sk)
    ((match-each ((pat var) . bindings) sk fail)
     (record-match var
       (pat (match-each bindings sk fail))
       (_   (fail))))))

(define-syntax if-mutable-entry?    
  (syntax-rules ()
    ((if-mutable-entry? (label . rest) name sk fk)
     (name ("mutable?") label sk fk)))) 

; Boxes for mutable fields
                      
(define (make-box x) (cons x '()))
(define box? pair?)              
(define unbox car)
(define (set-box! box value) (set-car! box value))


;====================================================================
; Internal syntax utilities:

(define-syntax syntax-error (syntax-rules ()))

(define-syntax syntax-apply
  (syntax-rules ()
    ((syntax-apply (f . args) exp ...) 
     (f exp ... . args))))

(define-syntax if-free=
  (syntax-rules ()
    ((if-free= x y kt kf)
      (let-syntax
          ((test (syntax-rules (x)
                   ((test x kt* kf*) kt*)
                   ((test z kt* kf*) kf*))))
        (test y kt kf)))))

(define-syntax top:if-free=
  (syntax-rules ()
    ((top:if-free= x y kt kf)
     (begin
       (define-syntax if-free=:test
         (syntax-rules (x)
           ((if-free=:test x kt* kf*) kt*)
           ((if-free=:test z kt* kf*) kf*)))
       (if-free=:test y kt kf)))))

(define-syntax syntax-cons
  (syntax-rules ()
    ((syntax-cons x rest k) (syntax-apply k (x . rest)))))

(define-syntax syntax-cons-after
  (syntax-rules ()
    ((syntax-cons-after rest x k) (syntax-apply k (x . rest)))))

(define-syntax syntax-car
  (syntax-rules ()
    ((syntax-car (h . t) k) (syntax-apply k h))))

(define-syntax syntax-cdr
  (syntax-rules ()
    ((syntax-cdr (h . t) k) (syntax-apply k t))))

(define-syntax syntax-foldr
  (syntax-rules ()
    ((syntax-foldr accum (f arg ...) () k)
     (syntax-apply k accum))
    ((syntax-foldr accum (f arg ...) (h . t) k)
     (syntax-foldr accum (f arg ...) t
       (f h arg ... k)))))

(define-syntax syntax-map
  (syntax-rules ()
    ((syntax-map () (f arg ...) k)      (syntax-apply k ()))
    ((syntax-map (h . t) (f arg ...) k) (syntax-map t (f arg ...)
                                          (syntax-map (f arg ...) h k)))
    ((syntax-map done (f arg ...) h k)  (f h arg ...
                                          (syntax-cons done k)))))

(define-syntax syntax-append
  (syntax-rules ()
    ((syntax-append (a ...) (b ...) k) (syntax-apply k (a ... b ...)))))

(define-syntax syntax-append-all
  (syntax-rules ()
    ((syntax-append-all lists k) 
     (syntax-foldr () (syntax-append-after) lists k))))

(define-syntax syntax-append-after
  (syntax-rules ()
    ((syntax-append-after y x k) (syntax-append x y k))))

(define-syntax syntax-filter
  (syntax-rules ()
    ((syntax-filter () (if-p? arg ...) k)
     (syntax-apply k ()))
    ((syntax-filter (h . t) (if-p? arg ...) k)
     (if-p? h arg ...
            (syntax-filter t (if-p? arg ...) (syntax-cons-after h k))
            (syntax-filter t (if-p? arg ...) k)))))  

(define-syntax syntax-zip
  (syntax-rules ()
    ((syntax-zip () () sk fk)             (syntax-apply sk ()))
    ((syntax-zip () lst* sk fk)           fk)
    ((syntax-zip lst () sk fk)            fk)
    ((syntax-zip (h . t) (h* . t*) sk fk) (syntax-zip t t* 
                                                      (syntax-cons-after (h h*) sk) 
                                                      fk))))  

(define-syntax add-temporaries   
  (syntax-rules () 
    ((add-temporaries lst k)                (add-temporaries lst () k))
    ((add-temporaries () lst-temps k)       (syntax-apply k lst-temps))
    ((add-temporaries (h . t) (done ...) k) (add-temporaries t (done ... (h temp)) k)))) 
                                                                                               
(define-syntax push-result
  (syntax-rules ()
    ((push-result x stack k) (syntax-apply k (x . stack)))))

(define-syntax remove-duplicates
  (syntax-rules ()
    ((remove-duplicates lst compare? k)
     (remove-duplicates lst () compare? k))
    ((remove-duplicates () done compare? k)
     (syntax-apply k done))
    ((remove-duplicates (h . t) (d ...) compare? k)
     (if-member? h (d ...) compare? 
                 (remove-duplicates t (d ...) compare? k)
                 (remove-duplicates t (d ... h) compare? k)))))

(define-syntax if-member?
  (syntax-rules ()
    ((if-member? x () compare? sk fk) 
     fk)
    ((if-member? x (h . t) compare? sk fk)
     (compare? x h
               sk
               (if-member? x t compare? sk fk)))))

(define-syntax recolor
  (syntax-rules ()
    ((recolor compare labels () k)
     (syntax-apply k ()))
    ((recolor compare labels (h . t) k)
     (recolor compare labels h 
      (recolor compare labels t "combine" k)))
    ((recolor h-done compare labels to-do "combine" k)
     (recolor compare labels to-do
      (syntax-cons-after h-done k)))
    ((recolor compare labels id k)
     (get-equiv compare id labels k))))

(define-syntax get-equiv
  (syntax-rules ()
    ((get-equiv compare label () k)
     (syntax-apply k label))
    ((get-equiv compare label (h . t) k)
     (compare h label
              (syntax-apply k h)
              (get-equiv compare label t k)))))

(define-syntax if-symbol?
  (syntax-rules ()
    ((if-symbol? (x . y) sk fk) fk)
    ((if-symbol? #(x ...))      fk)
    ((if-symbol? x sk fk)
     (let-syntax ((test (syntax-rules ()
                          ((test x sk* fk*)     sk*)
                          ((test non-x sk* fk*) fk*))))
       (test foo sk fk)))))

;=============================================================================
; End of reference implementation
;=============================================================================

Tests and examples

;==============================================================================
; Tests:
;=============================================================================

; A simple record:

  (define-record-type point (make-point x y) point?
    (x get-x set-x!)           
    (y get-y set-y!))                    

  (define p (make-point 1 2))
  (get-y  p)                                 ;==> 2
  (show
   (set-y! p 3))                             ;==> (point (x 1) (y 3))                              
  (point? p)                                 ;==> #t

; Subtyping:

  (define-record-type color make-color color?      ; default argument order is declaration order
    (hue hue set-hue!))                            ; notice punning

  (define-record-type (color-point color point)   
    (make-color-point x y hue)  ; differs from default ordering and arity
    color-point?
    (info info))                ; additional field left undefined by constructor

  (define cp (make-color-point 1 2 'green))
  (color-point? cp)                          ;==> #t
  (point? cp)                                ;==> #t
  (color? cp)                                ;==> #t
  (get-x  cp)                                ;==> 1
  (hue    cp)                                ;==> green
  (info   cp)                                ;==> <undefined>

; Labeled record expressions:

  (show
   (color-point (info 'hi) (x 1) (y 2)))  
            
                 ;==> (color-point (hue <undefined>) (x 1) (y 2) (info hi))

; Record update

  (define-record-type point2          #f #f (x #f #f) (y #f #f))
  (define-record-type (point3 point2) #f #f (x #f #f) (y #f #f) (z #f #f))
  
  (define p (point3 (x 1) (y 1) (z 3)))

  (show (record-update  p point2 (y 5)))  ;==> (point3 (x 1) (y 5) (z 3))  -- polymorphic update
  (show p)                                ;==> (point3 (x 1) (y 1) (z 3))  -- original unaffected
  (show (record-update* p point2 (y 5)))  ;==> (point2 (x 1) (y 5))        -- monomorphic update
  (show p)                                ;==> (point3 (x 1) (y 1) (z 3))  -- original unaffected
  (show (record-update! p point2 (y 5)))  ;==> (point3 (x 1) (y 5) (z 3))  -- destructive update
  (show p)          

; Record composition:

  (define c (color (hue 'green)))
  (define p (point (x 1) (y 2)))

  (show 
   (record-compose ((point p)
                    (color c))
     (color-point (x 5)
                  (info 'hi))))  ;==> (color-point (hue green) (x 5) (y 2) (info hi))
  
; Another record composition example:  

  (define-record-type monoid #f #f 
    (mult monoid.mult) 
    (one  monoid.one))
  
  (define-record-type abelian-group #f #f 
    (add  group.add) 
    (zero group.zero)
    (sub  group.sub))
  
  (define-record-type ring #f #f
    (mult ring.mult) 
    (one  ring.one)
    (add  ring.add) 
    (zero ring.zero)
    (sub  ring.sub))
  
  ; A simple functor

  (define (make-ring g m)
    (record-compose ((monoid        m)
                     (abelian-group g))
      (ring)))
  
  (define integer-monoid (monoid (mult *) 
                                 (one  1)))
  
  (define integer-group (abelian-group (add  +)
                                       (zero 0)
                                       (sub  -)))
  
  (define integer-ring (make-ring integer-group 
                                  integer-monoid))
  
  ((ring.add integer-ring) 1 2)    ;==> 3
  
; A tree record type:
  
  (define-record-type node (make-node lhs rhs) node?
    (lhs node.lhs)
    (rhs node.rhs))

  (define-record-type leaf (make-leaf val) leaf?
    (val leaf.val))
  
  (define (tree->list t)
    (cond
      ((leaf? t) (leaf.val t))
      ((node? t) (cons (tree->list (node.lhs t))
                       (tree->list (node.rhs t))))))
  
  (define t (make-node (make-node (make-leaf 1)
                                  (make-leaf 2))
                       (make-leaf 3)))

  (tree->list t)              ;==> ((1 . 2) . 3)
  
; Test pattern matching stub: 
  
  (record-match (color-point (x 1) (y 2) (hue 'blue))
    ((point (x a) (y b))
     (list a b)))             ;==> (1 2)

 (define (tree->list t)
   (record-match t
     ((leaf : v)   v)
     ((node : l r) (cons (tree->list l)
                         (tree->list r)))))

  (tree->list t)              ;==> ((1 . 2) . 3)
  
;=======================================================================================  

References

[1] Richard Kelsey, Defining Record Types, SRFI-9: http://srfi.schemers.org/srfi-9/srfi-9.html

[2] See e.g.
    Benjamin C. Pierce, Types and Programming Languages, MIT Press 2002, and references therein.
    Mitchell Wand, Type inference for record concatenation and multiple inheritance, 
                   Information and Computation, v.93 n.1, p.1-15, July 1991
    John Reppy, Jon Riecke, Simple objects for Standard ML,
                Proceedings of the ACM SIGPLAN '96 Conference on Programming Language Design and Implementation


Copyright

Copyright (C) Andre van Tonder (2004). All Rights Reserved.

This document and translations of it may be copied and furnished to others, and derivative works that comment on or otherwise explain it or assist in its implementation may be prepared, copied, published and distributed, in whole or in part, without restriction of any kind, provided that the above copyright notice and this paragraph are included on all such copies and derivative works. However, this document itself may not be modified in any way, such as by removing the copyright notice or references to the Scheme Request For Implementation process or editors, except as needed for the purpose of developing SRFIs in which case the procedures for copyrights defined in the SRFI process must be followed, or as required to translate it into languages other than English.

The limited permissions granted above are perpetual and will not be revoked by the authors or their successors or assigns.

This document and the information contained herein is provided on an "AS IS" basis and THE AUTHOR AND THE SRFI EDITORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.


Author: André van Tonder
Editor: David Van Horn
Last modified: Tue Mar 16 19:01:34 EST 2004