make-record-constructor-descriptor(3scm) Scheme Programmer's Manual make-record-constructor-descriptor(3scm)

make-record-constructor-descriptor, record-constructor - procedural record construction

(import (rnrs))                     ;R6RS
(import (rnrs records procedural))  ;R6RS

(make-record-constructor-descriptor rtd parent-rcd protocol)
(record-constructor rcd)

Returns a record-constructor descriptor (or rcd for short) that specifies a record constructor (or constructor for short). The constructor can used to construct record values of the type specified by rtd and it can be obtained via record-constructor.
Record type descriptors
The rtd argument must be a record-type descriptor created by make-record-type-descriptor(3scm).
Protocols and base record types
The protocol argument must be a procedure or #f. If it is #f, a default protocol procedure is supplied. If protocol is a procedure, it is handled analogously to the protocol expression in a define-record-type(3scm) form.
If rtd is a base record type then parent-rcd must be #f. In this case, protocol is called by record-constructor with a single argument p, which is a procedure that expects one argument for every field of rtd and returns a record with the fields of rtd initialized to these arguments.
The procedure returned by protocol should call p once with the number of arguments p expects and return the resulting record as shown here:
(lambda (p)
  (lambda (v1 v2 v3)
    (p v1 v2 v3)))
    
Here, the call to p returns a record whose fields are initialized with the values of v1, v2, and v3. The expression above is equivalent to (lambda (p) p).
Note that the procedure returned by protocol is otherwise unconstrained; specifically, it can take any number of arguments.
Single inheritance of record types
A constructor descriptor can also be used to create other constructor descriptors for subtypes of its own record type by passing it as parent-rcd.
If rtd is an extension of another record type parent-rtd and protocol is a procedure, parent-rcd must be a constructor descriptor of parent-rtd or #f.
If parent-rcd is a constructor descriptor, protocol is called by record-constructor with a single argument n, which is a procedure that accepts the same number of arguments as the constructor of parent-rcd and returns a procedure p that, when called, constructs the record itself. The p procedure expects one argument for every field of rtd (not including parent fields) and returns a record with the fields of rtd initialized to these arguments, and the fields of parent-rtd and its parents initialized as specified by parent-rcd.
The procedure returned by protocol should call n once with the number of arguments n expects, call the procedure p it returns once with the number of arguments p expects and return the resulting record. A simple protocol in this case might be written as follows:
(lambda (n)
  (lambda (v1 v2 v3 x1 x2 x3 x4)
    (let ((p (n v1 v2 v3)))
      (p x1 x2 x3 x4))))
    
This passes arguments v1, v2, v3 to n for parent-rcd and calls p with x1, x2, x3, x4 to initialize the fields of rtd itself.
Thus, the constructor descriptors for a record type form a sequence of protocols parallel to the sequence of record-type parents. Each constructor descriptor in the chain determines the field values for the associated record type. Child record constructors need not know the number or contents of parent fields, only the number of arguments accepted by the parent constructor.
The default protocol
Protocol may be #f, specifying a default constructor that accepts one argument for each field of rtd (including the fields of its parent type, if any).
Specifically, if rtd is a base type, the default protocol procedure behaves as if it were (lambda (p) p).
If rtd is an extension of another type, then parent-rcd must be either #f or itself specify a default constructor, and the default protocol procedure behaves as if it were:
(lambda (n)
  (lambda (v1 ... vj x1 ... xk)
    (let ((p (n v1 ... vj)))
      (p x1 ... xk))))
    
The resulting constructor accepts one argument for each of the record type's complete set of fields (including those of the parent record type, the parent's parent record type, etc.) and returns a record with the fields initialized to those arguments, with the field values for the parent coming before those of the extension in the argument list.
In the example, j is the complete number of fields of the parent type, and k is the number of fields of rtd itself.
The default parent rcd
If rtd is an extension of another record type and parent-rcd is #f, parent-rcd is treated as if it were a constructor descriptor for the parent rtd of rtd with a default protocol.
Using record constructor descriptors
The record-constructor procedure calls the protocol of rcd and returns the resulting constructor for records of the rcd. The constructor is a procedure that takes as many arguments as are required by the protocol.

Implementation responsibilities: If protocol is a procedure, the implementation must check the restrictions on it to the extent performed by applying it as described when the constructor is called. An implementation may check whether protocol is an appropriate argument before applying it.

make-record-constructor-descriptor
Returns a single value; a record constructor descriptor.
record-constructor
Returns a single value; a procedure.

; This is a rather complex example from R6RS which
; demonstrates inheritance and protocols. A simpler
; example can be found in make-record-type-descriptor(3scm).
(define rtd1
  (make-record-type-descriptor
   'rtd1 #f #f #f #f
   '#((immutable x1) (immutable x2))))
(define rtd2
  (make-record-type-descriptor
   'rtd2 rtd1 #f #f #f
   '#((immutable x3) (immutable x4))))
(define rtd3
  (make-record-type-descriptor
   'rtd3 rtd2 #f #f #f
   '#((immutable x5) (immutable x6))))
(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 cd1
 (make-record-constructor-descriptor
   rtd1 #f protocol1))
(define cd2
 (make-record-constructor-descriptor
   rtd2 cd1 protocol2))
(define cd3
  (make-record-constructor-descriptor
    rtd3 cd2 protocol3))
(define make-rtd1 (record-constructor cd1))
(define make-rtd2 (record-constructor cd2))
(define make-rtd3 (record-constructor cd3))
(make-rtd3 1 2 3 4 5 6 7 8 9)
   =>  #[rtd3 x1: 3 x2: 5 x3: 9 x4: 11 x5: 15 x6: 17]

See make-record-type-descriptor(3scm).

This procedure is unique to the R6RS record system. An equivalent procedure can be found in SRFI-237.

This procedure can raise exceptions with the following condition types:
&assertion (R6RS)
The wrong number of arguments was passed or an argument was outside its domain. In particular, rtd must be a record-type descriptor, parent-rcd must be #f or an appropriate record-constructor descriptor as described above. The protocol argument should be #f or a procedure. However, see IMPLEMENTATION NOTES.

make-record-type-descriptor(3scm), define-record-type(3scm)

R6RS

This procedure first appeared in R6RS as part of the record system.

This page is part of the scheme-manpages project. It includes materials from the RnRS documents. More information can be found at https://weinholt.se/scheme/manpages/.

There is no record-constructor-descriptor? predicate in R6RS.
2023-08-03