[R6RS] revised draft of record srfi

William D Clinger will
Fri Jul 29 21:39:23 EDT 2005

I have some comments on the draft of the records SRFI,

In the Rationale: "particularly useful writing interpreters" should
be something like "particularly useful when writing interpreters".
"manipuation" should be "manipulation".

Second of the "Issues": in the definition of eq-hash-table, gc-count
occurs free.  I think it should be a fourth argument, or 0.

Specification of make-record-type-descriptor:
"in the sense a type" should be "in the sense that a type"

Specification of record-constructor:  "parent parent" should be "parent"

Specification of explicit-naming define-type:
"<Constructor name> is defined to" and
"<Predicate name> is defined to" should probably be something like
"<Constructor name> is defined to be" and
"<Predicate name> is defined to be".

I suspect that <record name> is also defined to be a compile-time
description, and that the define-type form is constrained to appear
only in contexts where a definition may appear.

The indentation is a little screwed up, just before the specification
of a parent <record clause>.

The loose spec for generative record type definitions implies that
programmers will often have to be careful to prevent such definitions
from being evaluated more than once.  That's fine with me.

Implicit-Naming Syntactic Layer: "tot he" should be "to the",
"can be understood its translation" should be "can be understood
by its translation", "preceeding" should be "preceding", and
"just some of names" should be "just some of the names".

Specification of record-type-uid:  The opening parenthesis has no
matching close-parenthesis.

Examples, Procedural layer:  (point-y p1) should evaluate to 2,
not to 1.

References:  "definining" should be "defining".

Some comments on Marc Feeley's comments:

1)  Although some languages have started out by pretending there
were not types aside from product types, Scheme did not and I
don't think we should adopt that pretense.  If the inconsistency
between "define-type" and "record-type" is truly annoying, then
it would be better to fix it by changing the name of "define-type"
than by replacing "record-type" by "type".  As Kent said, though,
the name isn't terribly important.

2)  The procedural interface should be easy for programs to
generate and parse, not easy for humans to read.  The syntactic
interface is the one that needs to be easy for humans to read
and to write.  To confuse the purposes of these two interfaces
would be a mistake.

3)  Yes, we could require record-type descriptors to be records,
and we probably did that in Larceny, but thinking about it makes
my head hurt: bootstrapping, reflection, greatest fixed points,
wondering whether there's a circular external syntax for records,
and so on.

I don't see how it would buy us anything for distributed
computing.  Passing records back and forth would require
marshalling in any case---some kind of external syntax---
and whether the external syntax uses a record syntax for
record-type descriptors is pretty independent of whether
record-type descriptors are records internally.

4)  Kent says this was an oversight, but I think it was
serendipitous.  The semantics of non-generative record types
make perfect sense regardless of whether the parent is generative,
so I don't see the purpose of disallowing non-generative record
types that inherit from generative record types.  I think the
best argument for disallowing it is that some people might be
surprised it's allowed.

5)  I don't see any argument for making "field specifiers" a
distinct type.  Perhaps this argument was inadvertently omitted
from Marc's message.

6)  With the kind of inheritance described in this SRFI, it is
natural to allow derived record types to have fields whose name
is the same as some field in the parent; indeed, it would be
unnatural to disallow that, because we don't want programmers
to have to know and avoid the names of all the fields in some
complex parent that might have hundreds or even thousands of
fields.  Allowing fields to have the same name within a single
record type definition is the right thing to do, because it
will simplify telescoping and other standard transformations
of the inheritance hierarchy.  If you disallowed fields with
the same name, you'd create problems akin to those in a lambda
calculus that disallowed (lambda (x) (lambda (x) x)).

7)  Specifying that records created by (record-constructor rtd)
are equal? if and only if they are eq? is entirely reasonable,
and is also the simplest thing to do.  It does not in any way
prevent an implementation from using records to represent pairs,
vectors, strings, and what have you.  The implementation has the
option of using a different record constructor whose existence
is not even hinted at by this SRFI, or of simply redefining the
equal? procedure to special-case the types for which the equal?
procedure should behave differently from eq?.

8)  IMO, the concept of type equality is pretty well defined,
although the wording could be improved.  Let me take Marc's
questions one by one:

    When are types A and B equal?

When the record-type descriptors that represent types A and B
are equal according to the eq? procedure.  (In the specification
of make-record-type-descriptor, it might be worth clarifying
that "only one record type with that uid", "must also be the
same", and "the same record-type descriptor" all refer to the
same in the sense of eq?.  With those clarifications, the
circumstances under which the record-type descriptors will
be eq? are pretty well spelled out in the SRFI.)

    Can the procedural interface create a type that is equal to
    a type created by the syntactic interface?

Yes, not only because the syntactic interface is most sensibly
implemented by macro-expansion into the procedural interface,
but because the specifications of make-record-type-descriptor
and define-type explain how to create a non-generative record
type, and the specification of make-record-type-descriptor
explains that concept quite clearly, modulo the parenthetical
clarification I suggested above.

    Are the initializers part of the type?

No.  (This is obvious from the fact that the syntactic interfaces
can be implemented by macros that expand into the procedural
interface.  This semantic clarity is one of the reasons it is
so important to have a relatively clean and simple procedural
base for the hairy syntax.)

    Is the ordering of the fields important?

Yes.  See, for example, the description of record-accessor.
(Note, however, that type equality is defined independently;
I don't think you'd be asking questions of this kind unless
you were expecting some kind of structural equality, which
isn't the case.  Type equality *does* imply a certain kind
of structural equality, but the reverse is not true.)

    Are the sealed and opaque flags important?

Yes, of course.  (Note, however, that type equality is defined
independently, and is not structural.)

    How does the presence of uid affect type equality?

See the discussion of this in the fifth paragraph of the
specification of make-record-type-descriptor.

    What is "a verbatim copy of the same record definition"?

A second occurrence of the same syntactic phrase within the
source code of a program.  It hardly matters, though, because
the semantics of evaluating a copy is the same as the semantics
of re-evaluating the original for non-generative calls to
make-record-type-descriptor, and the semantics is also the
same for in both cases for generative calls.

The only possible source of confusion that I see is for people
who use the syntactic interfaces.  The explicit ambiguity there
appears to be intended to allow implementations the freedom to
create certain types at macro expansion time rather than run
time.  This ambiguity exists only for the generative case,
however.  In light of the ambiguity, it is the programmer's
responsibility to make sure her code operates correctly no
matter how the ambiguity is resolved.

It wouldn't bother me a bit to require a different type to be
generated at each execution, but I suspect that Kent Dybvig
can give us some very good reasons, quite possibly having to
do with separate compilation, for wanting the freedom to do
some of this at macro expansion time.  I'm okay with that.

9)  I would not be averse to dropping the init! <record clause>,
but I can see why it is useful and how it would encourage people
to write code that can be compiled more efficiently.


More information about the R6RS mailing list