pre-srfi / reconciled-records

Submitted as SRFI 237
0 stars 0 forks source link

Support SRFI 99 single-inheritance and maybe auto-identifier syntax #1

Open dpk opened 2 years ago

dpk commented 2 years ago

I think it should be possible in theory for the SRFI 9/R7RS small style of definition syntax to allow access to most of the same features provided by the R6RS style, using the SRFI 99 conventions.

In particular, according to the current spec, record types defined in SRFI 9 style can’t use inheritance. Fixing this requires changing the <name> in the SRFI 9 style to also be allowed to be (<name> <parent>), where <name> is an identifier and <parent> is any expression (that evaluates to an RTD). I don’t think this introduces any ambiguity with the R6RS style. This is the only actually semantically-significant feature missing from the SRFI 9 style at present, compared to SRFI 99; the rest is just sugar.

The other SRFI 99 thing is the ability to generate make-record, record?, record-field, and record-field-set! identifiers automatically. But supporting the complete SRFI 99 syntactic layer define-record-type would make it ambiguous which of the two define-record-type forms was intended in the case that all arguments are identifiers, and possibly in other cases too. The SRFI 131 form (i.e. just nixing the ability to use auto-generated names) is unambiguous, afaict.

Then there’s also the question of how the SRFI 9 style might support features like sealed, opaque, nongenerative, etc. I’m not sure this is nearly as important — people can just use the R6RS style if they need those features, I guess.

mnieper commented 2 years ago

Thanks for sharing your thoughts, Daphne.

A syntactic extension to allow (<name> <parent>) instead of <name> in R7RS-style record type definitions would in fact be possible. However, in such an extension, <parent> shouldn't be an expression (evaluating to a record-type descriptor), but a record name because that is what R7RS-style record type definitions bind (and what is preferably in general when efficiency matters).

We cannot support the SRFI 99/131 semantics, though, because they are incompatible with R7RS-small and "hygienic field" names. Ignoring this would reintroduce the problems this proposal tries to solve.

But even when we are a bit flexible with the semantic interpretation of the extended syntax, It's not so clear what the meaning of

(define-record-type (bar foo)
  (make-bar x y)
  (y bar-y)
  (w bar-w bar-w-set!))

should be. Does y in the constructor template correspond to the field y or to a field in the parent? But which one? A possible solution seems to demand that the first arguments of the constructor template are all dummy arguments and that there have to be as many dummy arguments as the parent constructor takes and that any further argument is matched against the field names of the child record-type definition. This clashes, however, with the more general R6RS-style definition where the arity of a constructor is not necessarily well-defined.

As to the extra features: nongenerative is important and 99% of the time you want your record types to be nongenerative. sealed is important for efficiency in case of inheritance.

dpk commented 2 years ago

The requirement in small is:

⟨name⟩ [i.e. the first argument to define-record-type] is bound to a representation of the record type itself. This may be a run-time object or a purely syntactic representation. The representation is not utilized in this report, but it serves as a means to identify the record type for use by further language extensions.

I think for the sake of R7RS Large we can take this ‘representation’ to be a record type descriptor.

mnieper commented 2 years ago

The requirement in small is:

⟨name⟩ [i.e. the first argument to define-record-type] is bound to a representation of the record type itself. This may be a run-time object or a purely syntactic representation. The representation is not utilized in this report, but it serves as a means to identify the record type for use by further language extensions.

I think for the sake of R7RS Large we can take this ‘representation’ to be a record type descriptor.

Why would you want to? In that case, you would also need to supply a constructor-type descriptor.

The procedural interface, for which a rtd is necessary, is really only needed when you need to create new record type shapes at runtime. I can hardly think of a use case where such an rtd has then to be passed to a syntactic record-type definition; IMO that's far out of scope for R7RS-style record type definitions.

It makes much more sense to have the name bound to the representation of the record type as in 6.2 of the R6RS Scheme Libraries. (Unfortunately, R6RS does not really give a name to this binding, so I am just calling it a record name because it appears syntactically as in the report.)

dpk commented 2 years ago

I suspect we’re talking at cross purposes here because your rationale says:

a syntactically defined record type can inherit from a procedurally defined record type, and vice versa […] To inherit from a record type all that has to be exposed from it is either the (bound) record name or the record type descriptor (with or without a record constructor descriptor)

How does a syntactically-defined record type inherit from a procedurally-defined record type if the <parent> slot isn’t allowed to be any expression evaluating to an RTD?

mnieper commented 2 years ago

For that, you need the more general R6RS-style record-type definition syntax. There, you would use the parent-rtd clause but this also needs to be given a record-type constructor definition.

What I write in the rationale is a riposte to some wrong believes about R6RS record-types that were cited by people who voted against R6RS. I don't mean that every syntactic form is likewise suitable for that.

But... as I said before, the use cases of procedurally defined record types are minor and specialized (like when you are writing an interpreter and want to re-use the host scheme's record type system). And even then, a situation where then one wants to feed that dynamic record type in a syntactic record-type definition is hard to imagine. And even, even then, one could still use the more general R6RS form. (That one wants to use sealed or opaque is 100000% more likely anyway.)

Does it make sense to you?

dpk commented 2 years ago

Ah, once again I was not familiar enough with the details of R6RS.

SRFI 99 only has the concept of a record-type descriptor, no record-constructor descriptor. Since SRFI 99 is compatible with the procedural and inspection layers of R6RS, it seems possible that with the version here, it could be made partially compatible with the R6RS syntactic layer? (Modulo only the fact that we can’t support all the auto-identifier features of SRFI 99 because of the ambiguity with R6RS.) In other words, like SRFI 99, to get rid of the concept of the record-constructor descriptor.

mnieper commented 2 years ago

In general, you don't want runtime values to describe parent record types; for efficiency, you usually want to use compile-time values in your compile-time definitions. Thus, if the R7RS-style of `define-record-type´ should become a viable option when defining child record types, you want it to take a record name and not a record-type descriptor.

I'm not so much interested in SRFI 99's procedural layer because it doesn't offer anything beyond R6RS and because it is not part of R7RS. This SRFI proposal is about the syntactic layer anyway.

In any case, the question of how to interpret the constructor template (make-bar x y) in my above example of an R7RS-style child record type has to be solved first.