[R6RS] Records draft

Marc Feeley feeley
Wed Jun 22 07:50:07 EDT 2005

On 22-Jun-05, at 3:20 AM, Michael Sperber wrote:

> [BTW, your MUA has been mangling your emails recently, especially
> multi-column displays.]
> Marc Feeley <feeley at iro.umontreal.ca> writes:
>> 2) The short form is (define-record-type name formals...) and the
>> long form is (define-record-type (name constructor predicate)  
>> formals...).
>> Why parens in the second case and not in the first?  Only because  
>> formals is
>> a list and it would be ambiguous to put parens only around the  
>> constructor
>> and predicate. This strange syntax is caused by using a positional- 
>> only approach
>> to specifying the arguments of the define-record-type form.
> I'm not sure I understand the comment: First off, the
> DEFINE-RECORD-TYPE form is *not* positional-only, as can be seen in
> your first example:
>>        keyword-based                  positional-based (as proposed)
>>    (define-record-type point     (define-record-type point (x y)
>>      x                             (fields ((mutable x) x)
>>      y)                                    ((mutable y) y)))
>                                       ^^^^^^ keyword

Come on Mike, here I was talking about the constructor, predicate and  
being specified by position.  I'm advocating that only the record name
be specified by position and that the fields and other attributes  
come after
that, but not nested in a set of (useless) parens.  All record types  
a name and a list of fields.

> I think what you mean is keywords within the clauses of the FIELDS
> form.  The syntax we have certainly allows that, even though we didn't
> have any optional things we wanted to put in at the time.

Keywords within fields is one issue, but not the only one.  In your
proposal, you have to say either "mutable" or "immutable".  I think this
should be optional, and default to "mutable".  Moreover by putting
it first you give it a special status over other attributes of the
field, which I think is wrong.  Once again these problems are due
to a positional syntax.  The attributes "mutable" and "immutable" are
symbols (and the init expression is possibly a symbol) so you are
using position and parentheses to distinguish the different attributes.
What I'm advocating is a syntax where a field declaration would be
in one of the following forms, where "field-name" is the name of the  
and "...attributes..." is a possibly empty list of attributes:

  1)   field-name
  2)   (field-name ...attributes...)
  3)   (field-name getter-name ...attributes...)
  4)   (field-name getter-name setter-name ...attributes...)

Note that this syntax is compatible with SRFI 9 in the sense that it
allows you to specify the names of the getter and setter.  In case 3
the field is immutable.  In cases 1 and 2, the names of the
getter and setter are generated automatically.  Note that the
list of attributes always starts with a keyword so there is
never a conflict with the getter and setter names.  Attributes
would specify im/mutability, the init expression, and possibly
other implementation specific attributes (type of the field,
whether "write" prints the field or not, whether the field is
visited by equal? or not, etc).  The im/mutability attribute
would default to mutable, and the init expression would default
to the parameter of the constructor at the same position (i.e.
first field gets first parameter of constructor, etc.).

>   I'm
> certainly amenable to discussing alternatives for syntax within
> FIELDS, but I'm not sure it's really a productive way of spending our
> time (see below).

The syntax is important because it is what the programmers will use all
the time.  If the syntax is awkward or cumbersome, then they will tend
to use vectors (see below).

> As to why the first operand of DEFINE-RECORD-TYPE is the way it is:
> Our view (Kent's and mine, Mike Ashley was also there) was that just
> specifying the record-type name is a shorthand for specifying the
> names of the predicate and the constructor.  Keyword syntax for that
> would be possible, but that would make the DEFINE-SIMPLE-RECORD-TYPE
> (where these clauses would be required) more verbose (at least to the
> point where the three of us could figure it out), and would hurt the
> smoothness of the transition between the two.

Using keywords is more verbose in the atypical case that you
want to specify the name of the constructor and the predicate.
But in that atypical case, it is good to have the keywords to
remind you what is being specified (and as I said it also allows
you to specify one or the other which may be useful for abstract
types where, for example, the constructor would be #f).

By the way I dislike the two syntactic layers of record definition  
Can you give a rationale for them?

>>    I can imagine a future extension of the record definition form
>>    where more than one constructor can be specified.  But such an
>>    extension will be hard to tack-on to the syntax proposed.
> No, it will be trivial to tack on in essentially the same way as you
> propose, because the record clauses carry keywords.
>> 4) The name "define-record-type" is too long for my taste, I prefer
>>    "define-record" or simply "define-type".  How sweet it would be to
>>    have lightweight (and easily understandable) code like this:
>>      (define-type point x y)
> I disagree that it is easily understandable

In my experience, the typical case for records is that you have a  
small number
of fields, and the constructor accepts the value of these fields in a
positional way.  The record definition form has to handle this case  
(i.e. with a straightforward and concise syntax) if we want to  
promote their
use in new code instead of the traditional short vectors.  With short  
you don't have to define anything, you just create your point with  
(vector x y)
and you access the components with vector-ref and vector-set!.  I'm  
we will keep seing that in code (including my own!) if the shortest  
for a point is:

     (define-type point (x y) (fields ((mutable x) x) ((mutable y) y)))

> , but that's beside the
> point: We won't come up with with a record-type-definition form that
> everyone likes---the proliferation of different ones in the various
> implementations, and the discussion we're having right here proves
> that.  Arguing about the "best" syntax on this list is therefore
> highly unlikely to get us anywhere.
> What we should do instead is provide a common infrastructure, and take
> a shot at providing a reasonable convenience layer.  That's why there
> are three layers in the proposal.  The convenience layer can't be and
> doesn't have to be perfect.  For Kent and myself, the "smooth upgrade
> path" between the primitive syntax layer and the convenience layer was
> important in taking that shot.
> Whatever convenience layer we provide, not everybody will be happy
> with it, and they can go and define their own.  Making this work:
> (define-type point x y)
> is trivial on top of of DEFINE-RECORD-TYPE.

I don't buy that argument.  We are designing a language for
people to use, and we want as many users to use this language
so that we (humans) can easily understand the code others have
written.  If the R6RS record definition syntax is so complex that
everyone implements their own sugar on top then we have failed.

>> 5) Finally, I see no mention of "final" (non extensible) record
>> types,
> I'd be happy to put that in.  I'm not sure it's a good default
> though---what if you want to extend a record-type definition sitting
> in a piece of code you can't change, where the author merely forgot to
> put in that additional keyword?

But a non extensible default makes the code more secure, in the sense
that the designer of a module has to explicitly say when it is OK to
subtype a record type that he defined.  Moreover, for locally used
records, which I think will be the typical case, there is no need for
the record type to be extended.  As an added bonus the compiler can
generate more efficient code.


More information about the R6RS mailing list