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

Re: Object creation discussion (at last!)

OK, here is a try at a counter proposal which tries to show what I do
and don't like about your proposal.  This isn't finished yet, once again
I thought it would be better to send it out now so that you could see
where I was headed.

Basically, my proposal keeps these features from your proposal:

  :initarg slot option
  :default-initargs defclass option (different syntax)
  make-instance processes slot initargs or evaluates initforms
  constructors can be faster because they can inline stuff

and gets rid of these:
  the new method combination type for initialize instance
  using &key in an initialize-instance method augments legal initargs


The :initarg slot option can be used to teach make-instance the name of
an initarg which can be used to initialize the value of this slot from
the initargs passed to make-instance.  The way to think of this option
is that it implements the abstract initarg name to slot name mapping for
make-instance.  For convenience, specifying an :initarg name in a slot
description also includes that initarg name in the :default-initargs of
the defclass.

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.  The syntax of
this option is a lot like the part of a lambda-list following &key
(which serves two similar purposes).  For example:

   (:default-initargs :foo :bar (:baz 3))

says that this class accepts initargs :FOO, :BAR and :BAZ.  Furthermore,
the default value of the :BAZ initarg is 3.  The default values of :FOO
and :BAR are both nil.

So, a defclass form like this:

   (defclass position ()
       ((x :initarg :x)
        (y :initarg :y))
     (:default-initargs (:x 0) (:y 0) :rho :theta))

can be read as saying:  make-instance with a first argument of POSITION
accepts 4 initargs (:x :y :rho :theta).  The default value for :x and :y
is 0; the default value for :rho and :theta is nil.  When make-instance
is called, it sets the value of the x slot of the instance to the value
of the :x initarg ..(same for y and :y).

make-instance calls allocate-instance generic-function with all the
initargs (the ones passed to make-instance plus any defaulted ones).  It
then sets the values of any slots for which initargs appear in the
initargs, it then sets the values of the remaining slots from their
initforms.  It then calls the intialize-instance generic-function with
all the initargs.

constructors can be thought of as providing a "boa" syntax for specific
calls to make-instance.  Because make-instance is a function and not a
generic-function, constructors are allowed to "perform the same actions
calling make-instance would have" rather than calling make-instance
directly.  As a result, in many implementations, constructors are faster
than the corresponding call to make-instance.  To produce the "call to
make-instance" a constructor interprets its lambda-list as follows:

  - if an element of its lambda-list names an initarg acceptable to
    this class, then that initarg pair will appear in the "call to

  - if an element of the lambda list does not name an initarg, but
    names a slot this is an abbreviation for defining a gensymed initarg
    for that slot and using that gensym.
    [This needs work or perhaps need to be removed.  I want to try
     to make this case be an abbreviation for something that is
     already understood.]

  - if neither of the above are true, calling the constructor signals
    an error.  Some implementations may warn earlier of course.

initialize-instance uses the default kind of method combination.  Note
that all initialize-instance methods should say &allow-other-keys.
Under some programming styles, many initialize-instance methods will be
:before methods.  Under other programming styles they will not be, they
will probably use call-next-method.