Records

18.1  Syntactic layer

While the syntactic layer could be expressed portably in terms of the procedural layer, standardizing a particular surface syntax facilitates communication via code.

Moreover, some implementations are able to implement the syntactic layer more efficiently than a portable implementation built on top of the procedural layer, and thus the syntactic layer facilitates the development of efficient portable libraries that define and use record types. The syntactic layer is designed to allow expansion- or compile-time determination of record characteristics (at least in the absence of a parent-rtd clause), including field offsets, so that, for example, record accesses can be be compiled efficiently to simple memory indirects. This property may be lost if the parent-rtd clause is present, and the parent is thus not generally known until run time.

18.2  Positional access and field names

The record and field names passed to make-record-type-descriptor and appearing in the syntactic layer are for informational purposes only, e.g., for printers and debuggers. In particular, the accessor and mutator creation routines do not use names, but rather field indices, to identify fields. Thus, field names are not required to be distinct in the procedural or syntactic layers. This relieves macros and other code generators from the need to generate distinct names.

The record and field names are used in the syntactic layer for the generation of accessor and mutator names, and thus duplicate field names may lead to accessor and mutator naming conflicts.

18.3  Lack of multiple inheritance

Multiple inheritance was considered but omitted from the records facility, as it raises a number of semantic issues such as sharing among common parent types.

18.4  Constructor mechanism

The constructor-descriptor mechanism is an infrastructure for creating specialized constructors, rather than just creating default constructors that accept the initial values of all the fields as arguments. This infrastructure achieves full generality while leaving each level of an inheritance hierarchy in control over its own fields and allowing child record definitions to be abstracted away from the actual number and contents of parent fields.

The constructor mechanism allows the initial values of the fields to be specially computed or to default to constant values. It also allows for operations to be performed on or with the resulting record, such as the registration of a record for finalization. Moreover, the constructor-descriptor mechanism allows the creation of such initializers in a modular manner, separating the initialization concerns of the parent types from those of the extensions.

18.5  Sealed record types

Record types may be sealed. This feature allows enforcing abstraction barriers, which is useful in itself, but also allows more efficient compilation.

In particular, when the implementor of an abstract data type chooses to represent that ADT by record type, and allows one of the classes that represent the ADT to be exposed and subclassed, then the ADT is no longer abstract. Its implementors must expose enough information to allow for effective subtyping, and must commit to enough of the representation to allow those subclasses to continue to work even as the ADT evolves.

In practice, the ADT cannot evolve freely over time. Its representation cannot be changed without breaking most of its subclasses. This kind of brittleness is often problematic in large programs. Sealedness allows the construction of record types that evolve more gracefully over time.

Moreover, making a record type sealed may prevent its accessors and mutators from becoming polymorphic, which would make effective flow analysis and optimization difficult. This is particularly relevant for Scheme implementations that use record to implement some of Scheme’s other primitive data types such as pairs.