[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Object creation discussion (at last!)



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.