I list a few things that are unclear to me from the spec.
Some may be omissions or rationale omissions, and some may be just my own
blindness :-)
- Why do the field /name/s in the procedural layer /not/ need to be distinct?
I could see this feature causing lots of pain. A rationale may help.
- Are the syntactic layer <field name>s identifier literals (like ELSE)
or implicitly quoted symbols? There is a big difference within the
context of macro behaviour, or even simply in the context of deciding the
meaning of things like
(let ((x 1))
(define-type foo (fields (x immutable))))
especially in the context of extensions that might use field names
for purposes such as pattern matching.
- Are the syntactic layer <field name>s the same as the /name/s
in the procedural layer? A priori they do not have to be. If they
are, it would imply that they are implicitly quoted symbols.
If they are not, they could still be symbols, or they could be identifiers.
In either case, do they have to be distinct, and if identifiers, under
what predicate (e.g. free-identifier=?, literal-identifier=?)?
- Can <field-name> be reused for <accessor-name> or <mutator-name> as in
(fields (x-coord (x-coord x-coord-set!))
or, for that matter, can <field-name> be bound somewhere else in the
program without breaking the record type?
- How does define-type bind <record-name> exactly? For example, in
(define-type foo ....)
(let ()
(define-type foo ....))
(type-descriptor foo)
which type descriptor will be returned? For uniformity with internal
defines, I would assume that internal define-types remain local and the
toplevel FOO would be returned. Is this correct?
- Is the procedural layer type descriptor /name/ the same as the
syntactic layer <record name>. This would imply that <record name> is
a symbol, which makes it unclear how DEFINE-TYPE binds it exactly
(since it is not symbols, but indentifiers, that are bound by binding forms).
The big problem I could see is that this would be incompatible with
locality of internal define-type's.
- Why do (sealed) and (opaque) have unnecessary parentheses?
- Why is INIT! a binding form? It forces one to eta-convert
reusable procedures, e.g. (init! (x) (display x)) instead of
just (init! display). It also builds a performance inefficiency
into the syntax.
- In the (nongenerative <uid>) clause, why not have a <uid-expression>
that can be evaluated. I can think of cases where you might, e.g., read
a previously stored uid from a database and wish to insert it in the
type declaration. The current syntax does not allow that.
(By the way, I think an evaluable <uid-expression> would actually
alow one to go the other way and make generative from nongenerative.)
- I do not understand the comment about the UUID namespace. The record
type is nongenerative after all, whereas UUID is concerned with generativity
(generating a never before seen ID).
Specifically, what problems may I run into if I do not use a UUID, e.g.
what is wrong with:
(define-type point ...
(nongenerative point))
- Why is generativity the default? I should think that normally, every
time I run a program, I would like my types to be the same, especially
if I read or write record values.
- Can I forward an unknown number of arguments to the parent?
- Will there be an external representation for records?
Cheers
Andre