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]
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.
This procedure first appeared in R6RS as part of the record system.
There is no record-constructor-descriptor? predicate in R6RS.