On records

Posted on September 10, 2015

I am reviewing some code in Vicare’s expander to clean up the internal representation of r6rs record types and related syntaxes (is-a?, slot-ref, slot-set!, …). All the changes discussed here are in the master branch.

Record–type name’s syntactic binding’s descriptor

Whenever we use the syntax define-record-type, a new syntactic binding is created using the record–type name as name of the syntactic identifier; its descriptor is a pair with format:

($record-type-name . ?value)

where the symbol $record-type-name is the “type” of the descriptor and ?value is its “value”.

Up until now, the ?value in the descriptor has been a list with the first two items being: the syntactic identifier bound to the record–type descriptor (rtd), the syntactic identifier bound to the default record–constructor descriptor (rcd); optionally another item can be present in the list, a record carrying further informations about the record–type.

I changed this and now the ?value is itself a record of type <r6rs-record-type-spec>; this way the syntactic binding descriptor can carry informations in a more ordered fashion. This should open the door to a better handling of types in the expander.

Syntactic interface to setting record destructors

I have added the clause destructor-protocol to the body of define-record-type; destructor-protocol is a new syntactic binding exported by the library (vicare). The syntax define-record-type accepts this clause only when the selected language is non–strict r6rs.

The definition clause:

(destructor-protocol ?expr)

allows the specification of an expression ?expr which must evaluate to the destructor protocol function; this function is used as explained below to construct a destructor function to be called:

It is possible for a destructor function to be applied multiple times to the same record: once a destructor is set in the descriptor, it can be explicitly applied to records and later applied again by the garbage collector. Destructor functions must be written in such a way that multiple applications are not a problem. For example, it is usually possible, upon destruction, to reset some record fields to the void object: when the destructor detects a field set to void, it knows that the record has already been finalised.

Here is how the destructor function is built:

Structs, record, Nausicaa’s classes, built–in object types, …

How many “record” objects are there in Vicare?

Vicare’s structs

Lightweight records providing: basic type disjunction (all the other records are in truth structs), simple “fields initialising” constructors, access to named slots, no inheritance, custom object printers, instance destructors.

r6rs records

Heavyweight records providing: type disjunction, access to named slots, simple inheritance, multiple construction protocols, custom object printers (as extension to r6rs), instance destructors (as extension to r6rs).

Nausicaa’s classes

Heavyweight records implemented by the library (nausicaa) providing: type disjunction, access to named slots, simple inheritance, multiple construction protocols, custom object printers, instance destructors, class methods, support for mixins, abstract types (class types whose public constructor raises an exception).

In addition to these both the tagged language half–written in the library (vicare) and the Nausicaa language implemented by the library (nausicaa) implement object types representing the “object–oriented” specification of built–in Scheme objects like fixnums, vectors, and others.

Are there too many of these? I am tempted to reorganise everything and:

That would be a major backwards–incompatible change. I will have to think more about this… in the meantime I will continue developing define-record-type trying to understand how much it can be extended without making the expander a huge mess.