Closed mnieper closed 8 years ago
This seems to be an error in R7RS small, which was supposed to be compatible with SRFI 9. Making field names anything other than symbols would be inconsistent with SRFI 9, SRFI 99, and SRFI 131.
So far as I can tell, the only use case that argues against field names being symbols is that of extending a record with some new fields when you don't know the field names of the record you're extending and are therefore in danger of giving of the new fields the same name as an existing field. In most languages, programmers who add new fields are supposed to know the names of fields in the parent. For this use case, making field names into identifiers instead of symbols is effectively the same as using a gensym, which is already available to Scheme programmers via string->symbol
.
With the requirement of SRFI 99 and SRFI 131 that child fields shadow parent fields with the same name, how can any danger arise when you are adding a new field with the same name as an existing field? The accessor and mutator procedures introduced by the parent record type definition will still access the parent field. The name of the field is (ignoring introspection) only relevant for matching corresponding fields in the constructor.
I think the only use case for field names being identifiers is when the parent wants to bar a child to set a field introduced by the parent in the constructor. (That said, it is not a real use case because setting the field versus not setting it makes no observable difference as the value of a non-set field is undefined, and could just be the value that would have been set.)
You're right: There is no real use case for having field names be identifiers.
Larceny doesn't handle the shadowing case correctly, and that's a real bug:
(import (srfi 131))
(define-record-type foo
(make-foo x y)
foo?
(x foo.x)
(y foo.y))
(define-record-type (bar foo)
(make-bar x y x)
bar?
(x bar.x))
(define b (make-bar 3 4 5))
(foo.x b) ; returns #!unspecified
Does your example make sense? SRFI 131 says: "It is an error if the same identifier appears more than once in the field names of the constructor spec." In the example, x
appears twice.
N.B.: By citing this bit of the specification, I am just noticing that the language of SRFI 131 is also a bit sloppy when it comes to the distinction between symbols and identifiers. So before including it into R7RS (large), the language should be made more precise.
P.S.: Apart from record types, there is another instance of this problem in R7RS (small): The definition of cond-expand
on the level of library bodies (not library definitions) matches everything hygienically as identifiers. But it does not make sense to match library names or features as identifiers. And the clause syntax library
would only match unbound identifiers with the same name.
You're right again: the (make-bar x y x)
is an error, and there's no way to repair the example because the field names need to be drawn from among the field names declared by the definition of bar
or those declared by one of its ancestors' field names.
Furthermore SRFI 131 says "If the record definition contains the same field name as one of its ancestors, it shadows the ancestor's field name for the purposes of the constructor. The constructor's argument initializes the child's slot, and the ancestor's slot of the same name is left uninitialized."
So Larceny's current behavior is correct, and there doesn't seem to be a real bug here (outside of R7RS-small, which Larceny can't fix).
This is the issue initially reported during the discussion in https://groups.google.com/forum/#!topic/scheme-reports-wg2/oKuhgwaM45w:
In SRFI 99 record types, record field names are matched by symbol name. In R7RS record types, record field names are matched hygienically as identifiers. However, Larceny uses SRFI 99 semantics also for the record types exported by
(scheme base)
.