[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Programmer-defined data types, version 2



Several people either proposed or supported a proposal that
RECORD-CONSTRUCTOR take a second argument, being a list of the slots
initialized by the new constructor, in the order of the actual arguments to
the constructor.  I support this change; it is reflected in the new version
of the proposal below.

In mail to the BASH list, Morry asked that this new second argument be
optional, defaulting to the relatively common case of all slots, in the
order specified in the call to MAKE-RECORD-TYPE.  I think I agree with that
and can appreciate his dissatisfaction with the prospect of having to
repeat the entire list of names again.  This modification of the above
proposal is also reflected below.

Kent Pitman (I wish others would be more specific when they quote ``Kent''
on this list; sometimes it's non-trivial to determine whether they're
talking about the ``Pitman'' or ``Dybvig'' flavor...) made a number of
suggestions concerning the default values of slots not initialized by a
constructor.  This discussion extended into what amounted to an
active-value facility, with hooks provided for notification when slots are
read or written.  My response is to say that it certainly seems as if such
facilities might be useful, but they are definitely beyond the scope of
this proposal.  The intent here is simply to provide the raw facilities for
user-defined data types.  Everything Kent suggests can be written in terms
of what's provided here and perhaps placed in the library; if they receive
widespread approval, perhaps we could later consider them for inclusion in
the language.

In a similar vein, Ziggy would like constructors to follow what amounts to
a Common Lisp keyword-style calling protocol, in which each slot value is
preceded in the argument list by the name of the slot.  This would be
inconsistent with every other procedure currently in Scheme (CONS comes to
mind as the best example), so I am reluctant to add it here.  Again, such a
facility could be implemented for the library.

Several people reported varying levels of nausea resulting from my use of
the term ``slot'' where, for example, ``field'' might have been less
traumatic.  I have made the stomach-settling change in the proposal below.

I made a note that ``the type-name argument to MAKE-RECORD-TYPE is
constrained to be a string in order to allow experimentation with
interesting semantics for other kinds of values there.  One possibility
raised in the discussion in 1988 was some kind of a `handler' procedure, as
in T objects.''  In reaction, Bawden commented, ``Even if someday you can
pass a ``handler'' to MAKE-RECORD-TYPE, mightn't you -also- want to pass a
type-name?  I don't see how the two are exclusive.''  I believe this is
handled in T by having the handler deal with an operation that provides the
type-name.  Thus, in such a system, passing a string would be equivalent to
passing a simple handler that only dealt with the fetch-type-name
operation.  In any case, I don't have a strong opinion about this; it does
not appear that Alan does either, so I've left this part of the proposal
unchanged.

Alan was the only person to comment on the semantics of EQUAL? when applied
to records of the same type, expressing the opinion that EQUAL? should
behave like EQV? in such cases.  I take others' silence on the point as
agreement and have so modified the proposal.

Norman would like to see all ``abstraction-breaking'' procedures so
identified in their description.  I think that this classification could
well be a matter of opinion on which some authors may disagree.  For the
time being, therefore, I have not followed up on this suggestion.  I
welcome comments.

Norman expressed the only opinion on whether or not a RECORD-COPIER
procedure should exist; Norman feels that it should not, being just one of
several possible operations generic to records.  I tend to agree and so
have removed the issue from the proposal.

Ziggy asked why, in the desciption of RECORD-TYPE-SLOT-NAMES, the returned
value is constrained to be EQUAL? to the list of names given to
MAKE-RECORD-TYPE.  This is necessary in order to use RECORD-CONSTRUCTOR
properly; it must be possible to discover the order of arguments to
constructors.  Of course, this is no longer necessary, since the given list
can now be passed as the second argument to RECORD-CONSTRUCTOR.  I have
therefore rewritten the restriction, below, to imply only set-equality of
the two lists.

I think that this covers all of the responses I've received thus far.  I'm
sure I'll hear about it if that's not the case...  Here is the new version
of the proposal.

=====================
We propose adding the following procedures to Scheme:

(MAKE-RECORD-TYPE type-name field-names)

Returns a ``record-type descriptor'', a value representing a new data type,
disjoint from all others.  The type-name argument must be a string, but is
only used for debugging purposes (such as the printed representation of a
record of the new type).  The field-names argument is a list of symbols
naming the ``fields'' of a record of the new type.  It is an error if the
list contains any duplicates.

(RECORD-CONSTRUCTOR rtd [field-names])

Returns a procedure for constructing new members of the type represented by
rtd.  The returned procedure accepts exactly as many arguments as there are
symbols in the given list, field-names; these are used, in order, as the
initial values of those fields in a new record, which is returned by the
constructor procedure.  The values of any fields not named in that list are
unspecified.  The field-names argument defaults to the list of field-names
in the call to MAKE-RECORD-TYPE that created the type represented by rtd;
if the field-names argument is provided, it is an error if it contains any
duplicates or any symbols not in the default list.

(RECORD-PREDICATE rtd)

Returns a procedure for testing membership in the type represented by rtd.
The returned procedure accepts exactly one argument and returns a true
value if the argument is a member of the indicated record type; it returns
a false value otherwise.

(RECORD-ACCESSOR rtd field-name)

Returns a procedure for reading the value of a particular field of a member
of the type represented by rtd.  The returned procedure accepts exactly one
argument which must be a record of the appropriate type; it returns the
current value of the field named by the symbol field-name in that record.
The symbol field-name must be a member of the list of field-names in the
call to MAKE-RECORD-TYPE that created the type represented by rtd.

(RECORD-UPDATER rtd field-name)

Returns a procedure for writing the value of a particular field of a member
of the type represented by rtd.  The returned procedure accepts exactly two
arguments: a record of the appropriate type and an arbitrary Scheme value;
it modifies the field named by the symbol field-name in that record to
contain the given value.  The returned value of the updater procedure is
unspecified.  The symbol field-name must be a member of the list of
field-names in the call to MAKE-RECORD-TYPE that created the type
represented by rtd.

(RECORD? obj)

Returns a true value if obj is a record of any type and a false value
otherwise.

(RECORD-TYPE-DESCRIPTOR record)

Returns a record-type descriptor representing the type of the given record.
That is, for example, if the returned descriptor were passed to
RECORD-PREDICATE, the resulting predicate would return a true value when
passed the given record.  Note that it is not necessarily the case that the
returned descriptor is the one that was passed to RECORD-CONSTRUCTOR in the
call that created the constructor procedure that created the given record.

(RECORD-TYPE-NAME rtd)

Returns the type-name associated with the type represented by rtd.  The
returned value is EQV? to the type-name argument given in the call to
MAKE-RECORD-TYPE that created the type represented by rtd.

(RECORD-TYPE-FIELD-NAMES rtd)

Returns a list of the symbols naming the fields in members of the type
represented by rtd.  The returned value contains precisely the set of
symbols in the field-names argument given in the call to MAKE-RECORD-TYPE
that created the type represented by rtd.

=====================
Notes on the proposal
=====================

-- The procedure RECORD? is necessary to allow reliable use of the
procedure RECORD-TYPE-DESCRIPTOR.

-- The type-name argument to MAKE-RECORD-TYPE is constrained to be a string
in order to allow experimentation with interesting semantics for other
kinds of values there.  One possibility raised in the discussion in 1988
was some kind of a ``handler'' procedure, as in T objects.

-- We do not propose any general macro for the definition of record types.
The feeling is that this is not easy to do in a way that is both elegant
and sufficiently flexible.  Once we have macros, the primitives above
should be sufficient for portable experimentation.

-- The issues of subtyping and inheritance, print methods, and integration
with modules and/or
environments (e.g. Pascal's WITH construct) have been purposely avoided in
order to achieve more rapid consensus.  Designs for adding single
inheritance appeared in the discussion in 1988.

-- A case can be made that constructor procedures should take no arguments
and leave all fields in new records uninitialized.  There appear to be
advantages to both points of view.

-- EQ? and EQV? treat records in the same way as they do pairs, vectors,
and strings.  EQUAL? is equivalent to EQV? on records.

	Pavel