{"size": 16273, "ext": "scrbl", "max_stars_repo_path": "doc/rnrs/records/syntactic.scrbl", "max_stars_repo_name": "david135/sagittarius-scheme", "max_stars_repo_stars_event_min_datetime": "2015-11-07T07:13:35.000Z", "max_stars_repo_stars_event_max_datetime": "2022-02-15T03:35:14.000Z", "max_issues_repo_path": "doc/rnrs/records/syntactic.scrbl", "max_issues_repo_name": "david135/sagittarius-scheme", "max_issues_repo_issues_event_min_datetime": "2016-04-28T21:59:59.000Z", "max_issues_repo_issues_event_max_datetime": "2021-07-01T08:42:21.000Z", "max_forks_repo_path": "doc/rnrs/records/syntactic.scrbl", "max_forks_repo_name": "david135/sagittarius-scheme", "max_forks_repo_forks_event_min_datetime": "2016-12-28T12:41:23.000Z", "max_forks_repo_forks_event_max_datetime": "2020-02-26T20:52:02.000Z"}
{"max_stars_count": 39, "max_issues_count": 3, "max_forks_count": 4, "avg_line_length": 41.4071246819, "max_line_length": 101, "alphanum_fraction": 0.7298592761}
@; -*- mode:scribble; coding: utf-8 -*-

@subsection[:tag "rnrs.records.syntactic.6"]{Records syntactic layer}

@define[Library]{@name{(rnrs records syntactic (6))}}
@desc{The @code{(rnrs records syntactic (6))}library. Some details of the
specification are explained in terms of the specification of the procedural
layer below.
}

@define[Macro]{@name{define-record-type} @args{name-spec record-clase @dots{}}}
@define["Auxiliary syntax"]{@name{fields} @args{field-spec @dots{}}}
@define["Auxiliary syntax"]{@name{parent} @args{parent-name}}
@define["Auxiliary syntax"]{@name{protocol} @args{expression}}
@define["Auxiliary syntax"]{@name{sealed} @args{boolean}}
@define["Auxiliary syntax"]{@name{opaque} @args{boolean}}
@define["Auxiliary syntax"]{@name{nongenerative} @args{:optional uid}}
@define["Auxiliary syntax"]{@name{parent-rtd} @args{parent-rtd parent-cd}}
@desc{[R6RS] A @code{define-record-type} form defines a record type along with
associated constructor descriptor and constructor, predicate, field accessors,
and field mutators. The @code{define-record-type} form expands into a set of
definitions in the environment where @code{define-record-type} appears; hence,
it is possible to refer to the bindings (except for that of the record type
itself) recursively.

The @var{name-spec} specifies the names of the record type, constructor, and
predicate. It must take one of the following forms:

@snipet{(@var{record-name} @var{constructor-name} @var{predicate-name})}
@snipet{@var{record-name}}

@var{Record-name}, @var{constructor-name}, and @var{predicate-name} must all be
identifiers.
@var{Record-name}, taken as a symbol, becomes the name of the record type.
(See the description of @see{make-record-type-descriptor}.) Additionally, it is
bound by this definition to an expand-time or run-time representation of the
record type and 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
(see @see{record-type-descriptor} and @see{record-constructor-descriptor}).

@var{Constructor-name} is defined by this definition to be a constructor for the
defined record type, with a protocol specified by the @code{protocol} clause, or,
in its absence, using a default protocol. For details, see the description of the
@code{protocol} clause below.

@var{Predicate-name} is defined by this definition to a predicate for the defined
record type.

The second form of @var{name-spec} is an abbreviation for the first form, where
the name of the constructor is generated by prefixing the record name with 
@code{make-}, and the predicate name is generated by adding a question mark 
(@code{?}) to the end of the record name. For example, if the record name is 
@code{frob}, the name of the constructor is @code{make-frob}, and the predicate
name is @code{frob?}
.
Each @var{record-clause} must take one of the auxiliary syntax forms; it is a
syntax violation if multiple @var{record-clauses} of the same kind appear in a
@code{define-record-type} form.

@snipet{(fields @var{field-spec}*)}

Each @var{field-spec} has one of the following forms

@snipet{(immutable @var{field-name} @var{accessor-name})}
@snipet{(mutable @var{field-name} @var{accessor-name} @var{mutator-name})}
@snipet{(immutable @var{field-name})}
@snipet{(mutable @var{field-name})}
@snipet{@var{field-name}}

@var{Field-name}, @var{accessor-name}, and @var{mutator-name} must all be identifiers.
The first form declares an immutable field called field-name>, with the corresponding
accessor named @var{accessor-name}. The second form declares a mutable field called
@var{field-name}, with the corresponding accessor named @var{accessor-name}, and
with the corresponding mutator named @var{mutator-name}.

If @var{field-spec} takes the third or fourth form, the accessor name is generated
by appending the record name and field name with a hyphen separator, and the mutator
name (for a mutable field) is generated by adding a @code{-set!} suffix to the
accessor name. For example, if the record name is @code{frob} and the field name
is @code{widget}, the accessor name is @code{frob-widget} and the mutator name is
@code{frob-widget-set!}.

If @var{field-spec} is just a @var{field-name} form, it is an abbreviation for
@code{(immutable @var{field-name})}.

The @var{field-names} become, as symbols, the names of the fields in the
@code{record-type} descriptor being created, in the same order.

The @code{fields} clause may be absent; this is equivalent to an empty @code{fields}
clause.

@snipet{(parent @var{parent-name})}

Specifies that the record type is to have parent type @var{parent-name}, where
@var{parent-name} is the @var{record-name} of a record type previously defined
using @code{define-record-type}. The record-type definition associated with
@var{parent-name} must not be sealed. If no parent clause and no @code{parent-rtd}
(see below) clause is present, the record type is a base type.

@snipet{(protocol @var{expression})}

@var{Expression} is evaluated in the same environment as the @code{define-record-type}
form, and must evaluate to a protocol appropriate for the record type being defined.

The protocol is used to create a record-constructor descriptor as described below.
If no @code{protocol} clause is specified, a constructor descriptor is still created
using a default protocol. The clause can be absent only if the record type being
defined has no parent type, or if the parent definition does not specify a protocol.

@snipet{(sealed @var{boolean})}

If this option is specified with operand #t, the defined record type is sealed,
i.e., no extensions of the record type can be created. If this option is specified
with operand #f, or is absent, the defined record type is not sealed.

@snipet{(opaque @var{boolean})}

If this option is specified with operand #t, or if an opaque parent record type is
specified, the defined record type is opaque. Otherwise, the defined record type is
not opaque. See the specification of record-rtd below for details.

@snipet{(nongenerative @var{uid})}
@snipet{(nongenerative)}

This specifies that the record type is nongenerative with uid @var{uid}, which must
be an identifier. If @var{uid} is absent, a unique uid is generated at macro-expansion
time. If two record-type definitions specify the same uid, then the record-type
definitions should be equivalent, i.e., the implied arguments to 
@code{make-record-type-descriptor} must be equivalent as described under
@code{make-record-type-descriptor}. If this condition is not met, it is either
considered a syntax violation or an exception with condition type @code{&assertion}
is raised. If the condition is met, a single record type is generated for both
definitions.

In the absence of a @code{nongenerative} clause, a new record type is generated
every time a @code{define-record-type} form is evaluated:

@codeblock[=> #f]{
(let ((f (lambda (x)
           (define-record-type r ...)
           (if x r? (make-r ...)))))
  ((f #t) (f #f)))
}

@snipet{(parent-rtd @var{parent-rtd} @var{parent-cd})}

Specifies that the record type is to have its parent type specified by
@var{parent-rtd}, which should be an expression evaluating to a record-type
descriptor, and @var{parent-cd}, which should be an expression evaluating to a
constructor descriptor. The record-type definition associated with the value of
@var{parent-rtd} must not be sealed. Moreover, a record-type definition must not
have both a @code{parent} and a @code{parent-rtd} clause.

All bindings created by @code{define-record-typ}e (for the record type, the
constructor, the predicate, the accessors, and the mutators) must have names that
are pairwise distinct.

The constructor created by a @code{define-record-type} form is a procedure as
follows:

@itemlist[
@item{If there is no @code{parent} clause and no @code{protocol} clause, the
constructor accepts as many arguments as there are fields, in the same order
as they appear in the @code{fields} clause, and returns a record object with
the fields initialized to the corresponding arguments.}
@item{If there is no @code{parent} or @code{parent-rtd} clause and a @code{protocol}
clause, the protocol expression must evaluate to a procedure that accepts a
single argument. The protocol procedure is called once during the evaluation of
the @code{define-record-type} form with a procedure @var{p} as its argument. It
should return a procedure, which will become the constructor bound to
@var{constructor-name}. The procedure @var{p} accepts as many arguments as there
are fields, in the same order as they appear in the fields clause, and returns
a record object with the fields initialized to the corresponding arguments.

The constructor returned by the protocol procedure can accept an arbitrary number
of arguments, and should call @var{p} once to construct a record object, and
return that record object.

For example, the following protocol expression for a record-type definition with
three fields creates a constructor that accepts values for all fields, and
initialized them in the reverse order of the arguments:
@codeblock{
(lambda (p)
  (lambda (v1 v2 v3)
    (p v3 v2 v1)))
}
}
@item{If there is both a @code{parent} clause and a @code{protocol} clause, then
the protocol procedure is called once with a procedure @var{n}as its argument.
As in the previous case, the protocol procedure should return a procedure, which
will become the constructor bound to @var{constructor-name}. However, @var{n} is
different from @var{p} in the previous case: It accepts arguments corresponding
to the arguments of the constructor of the parent type. It then returns a procedure
@var{p} that accepts as many arguments as there are (additional) fields in this
type, in the same order as in the @code{fields} clause, and returns a record object
with the fields of the parent record types initialized according to their constructors
and the arguments to @var{n}, and the fields of this record type initialized to
its arguments of @var{p}.

The constructor returned by the protocol procedure can accept an arbitrary number
of arguments, and should call @var{n} once to construct the procedure @var{p},
and call @var{p} once to create the record object, and finally return that record
object.

For example, the following protocol expression assumes that the constructor of
the parent type takes three arguments:

@codeblock{
(lambda (n)
  (lambda (v1 v2 v3 x1 x2 x3 x4)
    (let ((p (n v1 v2 v3)))
      (p x1 x2 x3 x4))))
}

The resulting constructor accepts seven arguments, and initializes the fields of
the parent types according to the constructor of the parent type, with @code{v1},
@code{v2}, and @code{v3} as arguments. It also initializes the fields of this
record type to the values of @code{x1}, @dots{}, @code{x4}.
}
@item{If there is a @code{parent} clause, but no @code{protocol} clause, then the
parent type must not have a @code{protocol} clause itself. The constructor bound
to @var{constructor-name} is a procedure that accepts arguments corresponding to
the parent types' constructor first, and then one argument for each field in the
same order as in the @code{fields} clause. The constructor returns a record object
with the fields initialized to the corresponding arguments.
}
@item{If there is a @code{parent-rtd} clause, then the constructor is as with a
@code{parent} clause, except that the constructor of the parent type is determined
by the constructor descriptor of the @code{parent-rtd} clause.
}
]

A protocol may perform other actions consistent with the requirements described
above, including mutation of the new record or other side effects, before returning
the record.

Any definition that takes advantage of implicit naming for the constructor,
predicate, accessor, and mutator names can be rewritten trivially to a definition
that specifies all names explicitly. For example, the implicit-naming record
definition:

@codeblock{
(define-record-type frob
  (fields (mutable widget))
  (protocol
    (lambda (p)
      (lambda (n) (p (make-widget n))))))
}

is equivalent to the following explicit-naming record definition.

@codeblock{
(define-record-type (frob make-frob frob?)
  (fields (mutable widget
                   frob-widget
                   frob-widget-set!))
  (protocol
    (lambda (p)
      (lambda (n) (p (make-widget n))))))
}

Also, the implicit-naming record definition:

@snipet{(define-record-type point (fields x y))}

is equivalent to the following explicit-naming record definition:

@codeblock{
(define-record-type (point make-point point?)
  (fields 
    (immutable x point-x)
    (immutable y point-y)))
}

With implicit naming, it is still possible to specify some of the names explicitly;
for example, the following overrides the choice of accessor and mutator names for
the widget field.

@codeblock{
(define-record-type frob
  (fields (mutable widget getwid setwid!))
  (protocol
    (lambda (p)
      (lambda (n) (p (make-widget n))))))
}
}

@define[Macro]{@name{record-type-descriptor} @args{record-name}}
@desc{[R6RS] Evaluates to the record-type descriptor (see 
@secref["rnrs.records.procedural.6"]{Records procedural layer}) associated with the type specified by
@var{record-name}.
}

@define[Macro]{@name{record-constructor-descriptor} @args{record-name}}
@desc{[R6RS] Evaluates to the record-type constructor (see
@secref["rnrs.records.procedural.6"]{Records procedural layer}) associated with the type specified by
@var{record-name}.

The following example uses the @code{record?} procedure from the
@code{(rnrs records inspection (6))} library:

@codeblock{
(define-record-type (point make-point point?)
  (fields (immutable x point-x)
           (mutable y point-y set-point-y!))
  (nongenerative point-4893d957-e00b-11d9-817f-00111175eb9e))
}
@codeblock{
(define-record-type (cpoint make-cpoint cpoint?)
  (parent point)
  (protocol (lambda (n)
                 (lambda (x y c) 
                   ((n x y) (color->rgb c)))))
  (fields (mutable rgb cpoint-rgb cpoint-rgb-set!)))
}
@snipet{(define (color->rgb c) (cons 'rgb c))}
@snipet{(define p1 (make-point 1 2))}
@snipet{(define p2 (make-cpoint 3 4 'red))}

@snipet[=> #t]{(point? p1)}
@snipet[=> #t]{(point? p2)}
@snipet[=> #f]{(point? (vector))}
@snipet[=> #f]{(point? (cons 'a 'b))}
@snipet[=> #f]{(cpoint? p1)}
@snipet[=> #t]{(cpoint? p2)}
@snipet[=> 1]{(point-x p1)}
@snipet[=> 2]{(point-y p1)}
@snipet[=> 3]{(point-x p2)}
@snipet[=> 4]{(point-y p2)}
@snipet[=> (rgb . red)]{(cpoint-rgb p2)}

@snipet[=> unspecified]{(set-point-y! p1 17)}
@snipet[=> 17]{(point-y p1)}

@snipet[=> (record-type-descriptor point)]{(record-rtd p1)}

@codeblock{
(define-record-type (ex1 make-ex1 ex1?)
  (protocol (lambda (p) (lambda a (p a))))
  (fields (immutable f ex1-f)))
}

@snipet{(define ex1-i1 (make-ex1 1 2 3))}
@snipet[=> (1 2 3)]{(ex1-f ex1-i1)}

@codeblock{
(define-record-type (ex2 make-ex2 ex2?)
  (protocol
    (lambda (p) (lambda (a . b) (p a b))))
  (fields (immutable a ex2-a)
           (immutable b ex2-b)))
}

@snipet{(define ex2-i1 (make-ex2 1 2 3))}
@snipet[=> 1]{(ex2-a ex2-i1)}
@snipet[=> (2 3)]{(ex2-b ex2-i1)}

@codeblock{
(define-record-type (unit-vector make-unit-vector unit-vector?)
  (protocol (lambda (p)
                 (lambda (x y z)
                   (let ((length (sqrt (+ (* x x) (* y y) (* z z)))))
                         (p (/ x length) (/ y length) (/ z length))))))
  (fields (immutable x unit-vector-x)
           (immutable y unit-vector-y)
           (immutable z unit-vector-z)))
}

@snipet{(define *ex3-instance* #f)}

@codeblock{
(define-record-type ex3
  (parent cpoint)
  (protocol (lambda (n)
                 (lambda (x y t)
                   (let ((r ((n x y 'red) t)))
                     (set! *ex3-instance* r)
                     r))))
  (fields  (mutable thickness))
  (sealed #t) (opaque #t))
}

@snipet{(define ex3-i1 (make-ex3 1 2 17))}
@snipet[=> #t]{(ex3? ex3-i1)}
@snipet[=> (rgb . red)]{(cpoint-rgb ex3-i1)}
@snipet[=> 17]{(ex3-thickness ex3-i1)}
@snipet[=> unspecified]{(ex3-thickness-set! ex3-i1 18)}
@snipet[=> 18]{(ex3-thickness ex3-i1)}
@snipet[=> ex3-i1]{*ex3-instance*}
@snipet[=> #f]{(record? ex3-i1)}
}
