On Sep 5, 2009, at 5:33 AM, Derick Eddington wrote:
On Thu, 2009-09-03 at 23:42 +0300, Abdulaziz Ghuloum wrote:
On Sep 3, 2009, at 9:30 AM, Derick Eddington wrote:
My wording in my last post below was hurried. Let me elaborate
better.
If you think "record type" is distinct from "record type
descriptor",
and "record type definition" is only via define-record-type and not
via
make-record-type-descriptor, then some of the wording/design in R6RS
might be more clear to you.
Yes, I do see them as different things. [I'm just elaborating here
for the general benefit; you might know all of this already.]
When you do (define-record-type foo), foo is not bound as a variable
nor as a macro, which may seem strange since R6RS does not say what
it really is.
It does seem strange, both because R6RS doesn't really say what it is
Right. All it says is that (record-type-descriptor foo) => rtd
and (record-constructor-descriptor foo) => rcd where rtd and rcd
are both values. In Ikarus, these two macros expand to variable
references that hold (at run time) the appropriate values.
[note that I can make foo expand to foo-rtd, but I think that
adds to the confusion]
or if it can be exported/imported like define and define-syntax
bindings,
In Ikarus at least, anything that can be defined can be exported
and imported and that includes record types, condition types,
enumeration types, modules, etc.
and because I think I want record types to be first-class.
Careful what you wish for ...
There, foo is bound to a record type: an expand-time
bindings identifying foo as a record type, and providing the expander
with information about the record type, most importantly: it's rtd
and rcd. You can obtain these two using (record-type-descriptor foo)
and (record-constructor-descriptor foo). If there is ever a need to
attach any more information to foo, the implementation can easily
attach it without needing to expose it in a separate name.
Couldn't a define-record-type macro, which binds the type name as a
run-time variable to an RTD, attach the expand-time info to the
identifier bound to the RTD, and then the expand-time info would be
attached to an identifier the same as it is now, but record types
would
also be first-class run-time. ?
Let's take a simpler example. Suppose you're designing an object
system. You get to choose whether you want your classes or class
definitions to be expand-time entities or run-time entities (or
both), right?
In the expand-time model, you would do
(define-class foo ---)
so that you attach the expand-time information to the foo identifier
and so that subsequent uses of foo (e.g., for inheritance) can, at
expansion time, request and make use of the expand-time information.
In the run-time model, you would do
(define foo (class ---))
so that class returns a first class object (a class descriptor) that
contains all the information about the class definition at run time.
[if you don't like the class example, use (define-c-procedure ---)
vs (define --- (c-procedure ---)), (define-signature ---) vs
(define foo (make-signature ---)), or your favorite example and the
same argument holds]
I advocate the first model because (1) it allows us to provide
information at macro expansion time by binding it to the foo
identifier directly, and (2) it allows us to also provide some
(possibly different) information about the class at run time
to support run-time operations (e.g., instantiation, etc.).
Note that the first model does not necessarily conflict with
having run-time operations on classes such as creating new
classes, etc.; it just says that expand-time operations are
defined on syntactic classes where the expand-time information
is available and not on procedurally-created classes since
no expand-time information is available about them.
Note also that I'm not talking about optimizations here, only
about what information is available at different times.
In the other model, when your classes are first-class objects,
all you can say about foo at expansion time is that it's a
variable and this is not enough. This means that you cannot,
for example, ask (at expansion time) whether foo is a class
definition, what its parent is, what its defined instance
variables are, and what operations it supports. Yes, foo is
a first-class object at run-time but that means that *all*
operations on it have to be performed at run time. This is
not always desirable.
This, from that intro to Chapter 6, is what makes me think it
definitely
gives the impression "record type" and "record type descriptor" are
the
same:
[...]
It says the procedural layer is for constructing record types.
What can I say? Sorry you've been mislead. :-)
Sorry for not citing him. His essay at [1] says what I said his main
point is and it says other things and it is quite convincing to me.
That's fine. They are not that convincing to me. :-)
According to Clinger [1], they don't have to be two separate things.
According to Sperber [2], Dybvig is/was for Clinger's changes.
I really cannot (or don't want to) dig into this, but remember that
R6RS is a compromise of the different parties and you cannot really
tell the real reason why one formal comment was accepted/rejected
without looking at the whole context (most of which was undocumented
in phone conferences and private mail archives, etc., that only a few
of us participated in).
If you insist that record types are ordinary variables bound to
values that may happen to hold rtds at run time, then you've just
given up on the ability to do anything with them at expansion
time.
According to Clinger [1], that's not true.
Hmmm. What Clinger says is:
The syntactic layer can deal with first-class values
by deferring them to run time, but the procedural
layer can't reach back in time to deal with macro
or expand-time values.
I find the first part of that statement unacceptable and the second
part untrue. Maybe you were referring to a different part of the
essay? (I can't find it)
If, as I mentioned above,
identifiers bound to an RTD had the same expand-time info attached to
them, then they could be queried at expand-time for the info exactly
the
same as what (I think) you're suggesting.
Maybe we're talking about the same thing, but maybe we're not.
I don't see why first-class types should be sacrificed.
In R6RS, rtds are still accessible at run time as first-class objects.
You can get them from a variety of sources. You can construct them
yourself using make-record-type-descriptor; you can get them from real
records using record-rtd; and you can obtain them from syntactically-
defined record types using record-type-descriptor. All I'm saying is
that R6RS also allows some more information to be attached to be
attached to the type in addition to what it's descriptor might be
(which might be a run-time computed value). Are you saying that
(define-record-type foo) should bind foo as an ordinary variable and
somehow attach some expand-time information to foo, or are you saying
that (define-record-type foo) should just expand to
(begin
(define foo (make-record-type-descriptor ---))
---)
and let the expander, somehow, be smart and attach some expand-time
information to foo?
I somewhat arbitrarily chose to make expand-time handles the default
and
automatically wrap them in record-type-descriptor in the match
expression expansion, because I assumed most users will use
define-record-type and I didn't want adding record-type-descriptor
to be
needed.
I think this is the correct assumption. As a matter of fact,
you can be safe ignoring the other layer completely. I cannot
believe anybody is using the procedural layer and making the
rtds manually and then wanting to use pattern matching (a
syntactic facility) to match on those.
I think some people have real uses for run-time creation of types. It
sure seems interesting to me, and if there's not a convincing reason
not
to have first-class types, then I'm all for them.
Again, I didn't say we should not have first-class types. All
I'm saying is that there are expand-time introspection facilities
that are pretty useful (and absent from R6RS) in addition to the
run-time introspection facilities that are also pretty useful.
The syntactic layer can provide syntactic information at expansion
time *and also* run-time information at runtime. The procedural
layer cannot provide any information at expansion time, thus it
requires that all computations happen at run time. This is what
Clinger (in [1]) advocates when he says:
[...] The solution is to
define a single standard notion of a record type,
and to use that one notion as the basis for both
the syntactic and the procedural layers.
To do that, of course, the standard notion of a
record type will have to be a first-class object.
The syntactic layer can deal with first-class values
by deferring them to run time, but the procedural
layer can't reach back in time to deal with macro
or expand-time values.
[some stuff I can't respond to at the moment for lack of time
deleted]
and how you cannot do
anything with a record without first creating a procedure
(predicate, accessor, mutator, etc) to do it for you.
I'm not sure I follow. How would you like this to be different?
Example for predicates:
Instead of saying:
(record-predicate <rtd>) => procedure
((record-predicate <rtd>) <object>) => boolean
I would just do
(record-has-type? <object> <rtd>) => boolean
same for the others. I like record-ref/record-set! instead
of ((record-accessor <rtd> <field>) <object>) and
((record-mutator <rtd> <field>) <object> <value>).
[side remark: Ikarus's current implementation of R6RS records
sucks. Originally, I was going to adapt the same strategy
of implementing structs which would've made at least the
syntactic layer of R6RS records very robust and efficient.
Unfortunately, Will Clinger's formal comment that resulted
in adding "parent-rtd" to the syntactic layer made that
implementation strategy far more difficult.
What formal comment was that?
Maybe I was mistaken and I apologize for that, but I thought formal
comment 90 <http://www.r6rs.org/formal-comments/comment-90.txt> was
the one that caused adding parent-rtd to define-record-type. Thinking
about it again, I recall that I only saw the parent-rtd field when
R6RS was finalized or shortly before that, so, maybe formal comment
90 was not the cause of it. A historian would know, and I cannot
dig right now.
In the long
term, this should not be a problem since ikarus's syntactic
define-record-type now only expands to the procedural layer
and thus optimizing the procedural layer should automatically
benefit the syntactic layer,
I believe this is how Clinger thinks it should be done (because of
Larceny's and SRFI 99's records design). But he also says the easier
expand-time info technique can still be done [1].
but this is an additional burden
on the implementor that (I believe) should not have been
there since the syntactic layer, by itself, is trivial to
optimize, while the procedural layer requires data-flow
analysis (across libraries to be most effective). As far as
I know, only Chez Scheme does some of that.]
If the expand-time info technique can still be done, then that burden
doesn't exist. Even if that burden still exists, I don't find it a
persuasive reason to break the abstraction of "record type" and to
sacrifice first-class run-time abilities.
Neither do I and I think I've explained my position well by now.
I'm not complaining about not being able to optimize it (if you
time it, you'd find that Ikarus's procedural layer is pretty
competitive with the other implementations, despite doing
absolutely no analysis or optimizations). All I'm saying is
that requiring that the syntactic layer be a thin layer on top
of the procedural layer is not a good idea regardless of speed
or whatever since it precludes having expand-time information.
[if you'd like to prove me wrong, I'd be happy to listen :-)]
For an optimizing compiler guy like yourself, requiring more non-
trivial
techniques increases demand for you :)
Heh! Remember also that optimizing compiler guys are pretty
scarce too, and if they waste their time optimizing record
accessors, well, they won't have time doing other things,
like responding to emails. :-)
I pretty much exhausted myself here, so, please let's do a
depth-first traversal of the discussion topics and not tackle
them all at once.
Aziz,,,