[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Object creation discussion (at last!)
- To: Moon@STONY-BROOK.SCRC.Symbolics.COM
- Subject: Re: Object creation discussion (at last!)
- From: Gregor.pa@Xerox.COM
- Date: 21 May 87 18:22 PDT
- Cc: Common-Lisp-Object-System@sail.stanford.edu
- In-reply-to: David A. Moon <Moon@STONY-BROOK.SCRC.Symbolics.COM>'s message of Mon, 27 Apr 87 01:30 EDT
I have elided parts of your message which I am not going to comment on
directly.
Date: Mon, 27 Apr 87 01:30 EDT
From: David A. Moon <Moon@STONY-BROOK.SCRC.Symbolics.COM>
Complexity and simplicity are partly matters of taste, and
largely matters of point of view.
I think this notion of what complexity and simplicity are is very
important to this discussion so, despite the fact that this is a
potential tarpit, I will try to make some comments about this.
I try to take the point of view
of the majority of programmers, who are using the outer interfaces
of the system without necessarily understanding everything that's
going on inside. No matter how beautifully we explain it, most
programmers simply won't read beyond the point where they think
they know everything they need to know to get their immediate job
done.
I agree.
Now, it may be that we will decide that we would rather make
life more complicated for people writing initialization methods in
order to make the conceptual explanation shorter.
Another way of saying this is "it may be that we will decide that we
would rather make it syntactically more complicated for people writing
initialization methods in order to make the conceptual explanation of
the system simpler". Let me try to explain why I would say it that way.
Because I agree with you that people will not read any more of the
documentation than they think they can get away with, I think it is
important for people to be able to develop mental models of the system
which have significant predictive power. By predictive power I mean
(among other things) it should be possible for a user to figure out how
to do something they haven't done before based on what they already
know. This means I believe it is often more important for a user to be
able to figure out how to do something than it is for there to be some
concise abbreviation for doing this 'new trick'.
Thus I believe that an important measure of a system's simplicity is the
ease with which users can develop a model of the system which has this
kind of predictive power.
In the example of initialization methods, my esthetic says that it would
be better for someone to be able to figure out how to write
initialization methods based on what they already know about ordinary
methods (without having to read any additional documentation) than it
would be for there to be some concise syntax for defining initialization
methods.
Note that this biases 'ease' away from the expert programmer. For the
record, I acknowledge this and this is consistent with my belief that it
is very difficult for an expert programmer to design something which is
really easy to understand. Expert programmers tend to design things
which have lots of 'bells and whistles' which make things simpler for
the expert user, but more complicated for everyone else. I will cite as
examples the Interlisp-D and LispM systems.
One key point that I was trying to convey, and I think
partially succeeded, was the need for information hiding
(abstraction) in the arguments to make-instance and in relations
among the various initialization specifications attached to a class
and its superclasses.
I agree that this is important, and your message does a good job of
collecting a lot of the reasons why this abstraction is important.
Remote initarg defaulting is related to this and your message does a
good job of describing why that is important.
Another key point, which I don't think came through, was that
each initarg should be explicitly defined, and should be defined in
exactly one place. This is simply good modularity.
I am not sure I agree with this. In fact, there are places later in
your message where you seem to disagree with this for the same reasons I
do.
Specifically, its not entirely clear to me what it means to 'define an
initarg'. Defining an initarg can mean specifying that this is a legal
initarg for a class (and its subclasses), or it can mean specifying what
the implementation of the processing this initarg should be. This is
like the protocol/implementation distinction. It would certainly be
'clean' for some sense of clean to require that legal initargs for a
class be specified in on option, and then there be other independent
mechanisms for specifying the implementation of those initargs
(corresponding to your types 1-4). One might argue that this is
cumbersome, but its important to understand that it would be simpler in
some senses.
--- reference ---
For reference, I am including the part of your message of April 16th
which defines initarg types 1-4.
From: David A. Moon
Date: Thu, 16 Apr 87 00:22 EDT
1. :allow-other-keys serves its usual function, as in all
&key argument lists. It isn't possible to define
additional initargs for step 1.
2. Initargs that control storage allocation are defined
by defining an allocate-instance method that accepts
the initarg as an &key argument. For example, in
systems with areas :area is an initarg for step 2.
The standard does not require any initargs to exist
for step 2.
3. Initargs that fill slots are defined by the :initarg
slot-option.
4. Initargs for initialization methods are defined by
defining an initialize-instance method that accepts
the initarg as an &key argument.
--- reference ---
All this leads to the idea that initargs of types 2 and 4 (in
the nomenclature of my 16 Apr message) should be defined by
methods, since their meaning is implemented by methods. Similarly,
type 3 should be defined by slot-options, since their meaning is
entirely involved with slots. In Flavors, type 4 have the ugly
property that they have to be defined in two places, once in the
method that implements them and again in a defflavor form; it's
easy to get these two places out of sync. Hence the proposal to
use the lambda-list of a defmethod as the way to define these
initargs.
As I said above, 'defining' in this paragraph can have two meanings.
The part of 'defining' which implements the behavior of the initarg
should definitely be close to 'the place that behavior affects or
happens'. So, slot setting initargs should have their behavior defined
in slots, initargs handled by methods should be in methods etc.
Along with this, I tried to eliminate clunky syntax from
initialization methods, hence the elimination of &allow-other-keys
and the elimination of having to write :before all the time. If
this is too confusing, we could invent a new syntax that both
defines initargs and defines the method that implements them.
Clearly this is the weakest part of my proposal.
As I said in my comments on what constitutes simplicity, I don't
necessarily agree that minor improvements in syntax (like removing
&allow-other-keys) always make for improvements in simplicity.
We could obviously simplify things a lot by getting rid of the
:constructor option to defclass. I didn't put it in, at least not
this year. I do think it ought to remain, in spite of its inherent
complexity in any initialization proposal (previous ones I've seen
have glossed over this rather than solving it), because I believe
many users will find it quite useful.
I don't know whether I like constructors yet or not. In my message last
week I tried to show that I think constructors are deceptively confsuing
because they contain a hidden performance optimization. I also think
they are deceptively confusing because the rules for breaking the
lambda-list down into initargs and setf of slot-value(s) are
complicated. Because :constructors are just an abbreviation for a defun
with a make instance and some setf of slot-values, I have separated them
from the rest of initialization for the time being. I want to think
about putting them back once we understand the rest better.
Date: Wed, 22 Apr 87 13:42 PDT
From: Gregor.pa@Xerox.COM
The special method combination type for initialize
instance adds considerable conceptual overhead for very little
functional gain.
The goal was not functional gain, that is, the ability to
program something you couldn't program before, but rather syntactic
simplicity. Perhaps in the end we'll decide it's not worth it, but
I'd like us to keep considering the question a bit longer.
My description of simplicity at the beginning of this message shows why
I think this extra method-combination type is not a net gain.
Date: Wed, 22 Apr 87 17:28 PDT
From: Gregor.pa@Xerox.COM
The default-initargs defclass option serves two
purposes. It specifies all the initargs which can be passed to
make-instance of this class, and it specifies default values
for some of those initargs.
This is an important modularity mistake, in my opinion. By
combining these two things, which ought to be separate, you have
lost the ability for one class to specify a default value for an
initarg defined by another class. If specifying a default value
always defines an initarg, there is no way to check the consistency
of the set of initargs with default values specified against the
set of initargs actually defined. If there is a misspelling, the
default value will simply be discarded, since no method will
receive it and do something with it.
I agree with this reasoning. This is the reasoning I was making
reference to earlier in this message when I said that there were reasons
not to 'define' initargs in one place.