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

Object Creation Writeup



Dick, this is the final draft of the Object Creation Writeup, unless
we send you any corrections on Tuesday.

-foo- means the word foo in italics.  FOO means the word foo in boldface.


>>>> New Writing 

Page 1-5   Creating Instances of Classes

[Replace existing text with this.   The goal is to give an overview of
creating instances.   It's too early to discuss initialization in detail
because we haven't discussed methods yet.]

The generic function MAKE-INSTANCE creates and returns a new instance of
a class.   CLOS provides a flexible means for specifying how a new
instance is initialized.   For example, users specify how to fill the
slots with values (either by giving an argument to MAKE-INSTANCE, or by
providing a default initial value).   Users can also write methods that 
perform extra initialization.    The complete initialization protocol is 
described in the section "Object Creation and Initialization".

Page 1-27:  New Chapter:  Object Creation and Initialization 

SECTION:  Overview 

The function MAKE-INSTANCE creates and returns a new instance of a
class.   The first argument is a class or the name of a class, and the
remaining arguments are an initarg ("initialization argument") list.
 
Initialization consists of several distinct steps, including:  combining
the explicitly supplied initargs with the default values for the
unsupplied initargs, checking the validity of the initargs, allocating
storage for the instance, filling slots with values, and executing
user-supplied methods that perform additional initialization.  CLOS
defines MAKE-INSTANCE in a procedural way, with each step represented by
a generic function.  This gives the user the ability to customize any
number of the steps.  In addition, MAKE-INSTANCE is a generic function,
allowing the user to replace the entire procedure if so inclined.

CLOS specifies default methods for each step, so there is a well-defined
standard behavior for the entire initialization procedure.  For many
programs, the standard behavior is appropriate.   The standard behavior 
provides users with four simple mechanisms for controlling 
initialization: 
  
 - Declaring a symbol to be an initarg for a slot, by 
   by using the :INITARG slot option. This allows one to
   provide a value for a slot in a call to MAKE-INSTANCE.  

 - Supplying a default value form for an initarg, by using the
   :DEFAULT-INITARGS class option.   This default value is used if the
   initarg is not explicitly provided as an argument to MAKE-INSTANCE.

 - Supplying a default value form for a slot, by using the :INITFORM
   slot option.  This default value is stored into the slot if no
   initarg associated with that slot is given as an argument to
   MAKE-INSTANCE or defaulted by :DEFAULT-INITARGS.

 - Defining methods for INITIALIZE-INSTANCE.   The slot-filling
   behavior described above is implemented by a system-supplied 
   default method for INITIALIZE-INSTANCE.  When users need 
   to exert greater control over initialization, they can provide
   methods for INITIALIZE-INSTANCE.   In most cases :AFTER methods 
   are appropriate for this purpose, because they are called 
   after the default method that fills the slots, and thus do not
   override the normal slot-filling behavior. 

Note that the object creation and initialization procedure can be
controlled at two different levels.   The standard behavior offers the 
four mechanisms mentioned above; this level can be considered the
Programmer Interface level.   At the Meta-object level, users can exert 
greater control over each step of this procedure; this level can be
considered the interface for experimentation with alternative
object-oriented paradigms.

There is one general guideline that distinguishes between the Programmer
Interface level and the Meta-object levels of programming.  To customize
behavior at the Programmer Interface level, the user defines methods
that specialize on instances.  That is, the arguments that select
methods are instances.   To customize behavior at the Meta-object level,
the user defines methods that specialize on classes.  That is, the
arguments that select methods are classes.
 
This chapter begins by describing the terminology and concepts of the  
standard initialization behavior, which is the Programmer Interface. 
This chapter then describes the procedural definition of MAKE-INSTANCE.
It briefly mentions the generic functions that implement each step of   
the procedure; this is the Meta-object level of initialiation.   The  
details of these functions are documented in Chapter 3.  

SECTION:  Terminology Related to Object Creation and Initialization 

The terminology related to object creation and initialization is
presented here:

-initarg-.  An initarg (initialization argument) is a keyword argument
that can be used to control object creation and initialization.  The
&key arguments to MAKE-INSTANCE are initargs.  It is often convenient to 
use keyword symbols to name initargs, but the name of an initarg can be
any symbol, including NIL.  There are two possible purposes for an
initarg:  to fill a slot with a value or to provide an argument for
an initialization method.   A single initarg can be used for more than
one purpose.

-initarg list-.  An initarg list (initialization argument list) is a
list of alternating initarg names and values.  Its structure is
identical to a property list and also identical to the portion of an
argument list processed for &key parameters.  As in those lists, if an
initarg name appears more than once in an initarg list, the leftmost
occurrence supplies the value and the remaining occurrences are ignored.
The arguments to MAKE-INSTANCE (after the first argument) are an initarg
list.  As in an &key argument list, :ALLOW-OTHER-KEYS can appear in an
initarg list, and if its value is non-NIL, error-checking of initarg
names is disabled.

-slot-filling initarg-.  An initarg associated with a slot.  If
the initarg has a value in the initarg list, the value is stored into
the slot of the newly-created object, overriding any initform associated
with the slot.  A single initarg can fill more than one slot.  A
slot-filling initarg that fills a shared slot stores its value into the
shared slot, replacing any previous value.  

-method-implemented initarg-.  An initarg associated with a method.   A
method-implemented initarg is intended as an argument for one or more
methods for INITIALIZE-INSTANCE or ALLOCATE-INSTANCE.  When an object is
created, the method is called with the initarg's value as an argument
and the method uses the value however it chooses.  If the initarg has no
value in the initarg list, the method's lambda-list supplies a default
value.

SECTION:   Declaring the Validity of Initargs 

MAKE-INSTANCE checks the validity of the initargs and signals an error
if an initarg is supplied that is not valid.  An initarg is declared 
as valid in the same place where its purpose (whether slot-filling or
method-implemented) is stated.

Slot-filling initargs are declared as valid by the :INITARG slot option
to DEFCLASS.  The :INITARG slot option is inherited from superclasses.
Thus, the set of valid slot-filling initargs for a class is the union of
the initargs declared by the class and its superclasses.

Method-implemented initargs are declared as valid by defining methods
for INITIALIZE-INSTANCE or ALLOCATE-INSTANCE.  The keyword name of each
keyword parameter specifier in the method's lambda-list becomes a
method-implemented initarg for all classes for which this method is
applicable.  Thus, method inheritance controls the set of valid
method-implemented initargs.

The set of valid initargs for a class is the union of the valid 
slot-filling initargs, the valid method-implemented initargs, and the 
pre-defined initarg :ALLOW-OTHER-KEYS.    The default for 
:ALLOW-OTHER-KEYS is NIL, and its specification is the same as Common 
Lisp defines for &KEY argument lists. 

SECTION:   Defaulting of Initargs

A -default value form- can be supplied for an initarg.    The way to
provide a default value form for either a slot-filling and
method-implemented initarg is to use the :DEFAULT-INITARGS class option.
A default value form is usually specified by a different class from the
class that declared the initarg as valid.  Thus, :DEFAULT-INITARGS is
usually used to supply a default value for an inherited initarg.

The :DEFAULT-INITARGS class option is inherited.   See ``Inheritance of 
Class Options''.

The :DEFAULT-INITARGS class option is followed by alternating initarg
names and forms.   Each form is the default value form for the
corresponding initarg.   The default value form of an initarg is used
only if that initarg does not appear in the arguments to MAKE-INSTANCE.
In that case, the default value form is evaluated in the lexical
environment of the DEFCLASS form that supplied it, and the resulting
value is used as the initarg's value.  The initarg name and value are
appended to the initarg list supplied to MAKE-INSTANCE.   The result is
a -defaulted initarg list- in which the explicitly supplied initargs
appear before the defaulted initargs.  Defaulted initargs are ordered
according to the order in the class precedence list of the classes
that supplied the default values.

The :DEFAULT-INITARGS option is used only to provide default values for 
initargs; it does not declare a symbol as a valid initarg name.

One should distinguish between the purposes of :DEFAULT-INITARGS and
:INITFORM, with respect to slot-filling initargs.   The
:DEFAULT-INITARGS class option allows the user to give a default value
form for an initarg without knowing whether or not the initarg fills a
slot.   If that initarg is not explicitly supplied in a call to
MAKE-INSTANCE, the default value form is used, just as if it had been
supplied in the call.   In contrast, the :INITFORM slot option allows
the user to give a default initial value form for a slot.  An :INITFORM
is used only if no initarg associated with that slot is given as an
argument to MAKE-INSTANCE or defaulted by :DEFAULT-INITARGS.  The two
kinds of defaulting exist at different levels of abstraction.

Note: CLOS does not guarantee any given order of evaluation of 
default-initarg forms and initforms.  If there are dependencies among
these forms, INITIALIZE-INSTANCE methods should be used instead.  In
most programs, the initforms and default-initarg forms are either
constants or simple forms that construct new objects; forms with
side-effects are permitted, but are not typically used.

SECTION:   Rules for Duplication of Initargs
 
The following rules specify what happens when initargs are duplicated in
various ways.   

 - The :INITARG slot option may be specified more than once for a given slot.  

 - A single initarg can initialize more than one slot if the same initarg name
   appears in more than one :INITARG slot option.

 - It is valid for a given initarg name to be defined more than once as a 
   slot-filling initarg, as a method-implemented initarg, or both.

 - If two initargs that initialize the same slot, with the same or different
   names, are given in the arguments to MAKE-INSTANCE, the leftmost of these
   initargs in the initarg list prevails.   This behavior is consistent
   with the behavior of property lists and the portion of an argument 
   list processed for &key parameters.

 - If two different initargs that initialize the same slot have default values,
   and neither is given explicitly in the arguments to MAKE-INSTANCE,
   the initarg that appears in a :DEFAULT-INITARGS slot option in the most
   specific class prevails, or if they appeared in the same class, the one whose
   mention in :DEFAULT-INITARGS is leftmost in the DEFCLASS form prevails.
   During the defaulting of initargs, the defaults are appended 
   to the end of the initarg list in this order.

 - If there are two different initargs that initialize the same slot, and one
   was given explicitly in the arguments to MAKE-INSTANCE while the other was
   defaulted via :DEFAULT-INITARGS, the explicit one prevails.  (This rule is
   implied by the two preceding rules, but it is worth mentioning
   explicitly.)
  
 - If a slot has both an :INITFORM and an :INITARG slot option, and the
   slot-filling initarg is defaulted via :DEFAULT-INITARGS, the initform is not
   used and is not evaluated.

An illustrative example of the above rules:

  (defclass a () ((x :initarg a)))
  (defclass b (a) ((x :initarg b))
    (:default-initargs a 1 b 2))

                                 DEFAULTED 
  FORM                          INITARG LIST    CONTENTS OF X SLOT
  (make-instance 'b)            (a 1 b 2)               1
  (make-instance 'b 'a 3)       (a 3 b 2)               3
  (make-instance 'b 'b 4)       (b 4 a 1)               4
  (make-instance 'b 'a 1 'a 2)  (a 1 a 2 b 2)           1

SECTION:   Methods for INITIALIZE-INSTANCE 

INITIALIZE-INSTANCE is a generic function that uses standard method 
combination.   Users can define methods for INITIALIZE-INSTANCE to
perform any initialization that cannot be achieved with the simple
slot-filling mechanisms.   

CLOS calls the generic function INITIALIZE-INSTANCE after it has:

  - Computed the defaulted initarg list by combining the  
    supplied initarg list with any default initargs   
    for the class.

  - Checked the validity of the defaulted initarg list.  If any 
    of the initargs has not been declared as valid, an error is
    signaled.

  - Created a blank instance.

CLOS then calls INITIALIZE-INSTANCE with the blank instance and the
defaulted initarg list.    The system-supplied default method is a
primary method that initializes the slots with values according to the
initarg list.  For each slot (whether local or shared):
 
  - If an initarg in the defaulted initarg list fills that slot, its 
    value is stored into the slot.  (This is true even if a :BEFORE method
    has modified the slot.)

  - Otherwise, if the slot is uninitialized and it has an initform, the
    initform is evaluated and the result is stored into the slot.

  - The duplicate-resolution rules mentioned in the section "Rules for
    Duplication of Initargs" are obeyed. 
 
Typically, user-defined methods are :AFTER methods; however that is not
a requirement.  Users should take care not to supply primary methods
that override the default primary method unless they want to prevent 
the normal slot-filling from occurring.

CLOS provides two functions that are useful in the bodies of
INITIALIZE-INSTANCE methods.   The function SLOT-BOUNDP returns a 
boolean value that states whether the slot is bound or not; this allows
for writing :AFTER methods for INITIALIZE-INSTANCE that initialize slots 
only if they have not already been initialized.   The function
SLOT-MAKUNBOUND restores a slot to the uninitialized condition. 

Implementations are permitted to make certain optimizations of
INITIALIZE-INSTANCE.   The description of INITIALIZE-INSTANCE in Chapter
2 mentions the possible optimizations.    One possible optimization has
the following impact on user-supplied methods:  :BEFORE and :AROUND
methods for INITIALIZE-INSTANCE cannot rely on all the slots being
uninitialized.

SECTION:   Procedural Definition of MAKE-INSTANCE

MAKE-INSTANCE behaves as if it were defined as follows, except that
certain optimizations are permitted: 

(defmethod make-instance ((class standard-class) &rest initargs)
  (setq initargs (default-initargs class initargs))
  (check-initargs class initargs)  
  (let ((instance (apply #'allocate-instance class initargs)))
    (apply #'initialize-instance instance initargs)
    instance))

(defmethod make-instance ((class-name symbol) &rest initargs)
  (apply #'make-instance (symbol-class class-name) initargs))

Users can customize this procedure at either the Programmer Interface
level, the Meta-object level, or both.
 
The Programmer Interface level includes using the :INITFORM, :INITARG,
and :DEFAULT-INITARGS options to DEFCLASS, and defining methods for
INITIALIZE-INSTANCE.  
 
The Meta-object level supports extra customization by defining methods
for:   DEFAULT-INITARGS, CHECK-INITARGS, and ALLOCATE-INSTANCE.  
Chapter 3 documents each of these generic functions and the
system-supplied default methods.

As noted above, certain optimizations of the MAKE-INSTANCE procedure are
permitted.  The description of INITIALIZE-INSTANCE in Chapter 2 mentions
some possible optimizations to this procedure.   Additional
optimizations are possible, including inlining and constant-folding of
method lookup and method bodies, provided that the programming
environment either prohibits redefining these methods or updates
everything when they are redefined.  One approach might be for
MAKE-INSTANCE to have a separate method for every class, which is
automatically written and compiled by the system.

Because of optimization, methods for the meta-object generic functions
listed may not actually be called on every call to MAKE-INSTANCE, or may
not receive exactly the arguments that would be expected.  For example,
CHECK-INITARGS might actually be called before DEFAULT-INITARGS rather
than after, if it has already been determined that the default initargs
will pass CHECK-INITARGS.

>>>> Mechanical Tasks:

Page 1-8:  inheritance of :initarg  

The :INITARG slot-option is inherited from superclasses.  The set of
initargs that initialize a given slot is the union of the sets of initargs declared
in :INITARG slot-options with the same slot name in the class and its
superclasses.  

Page-9:  inheritance of :default-initargs (the only class option that is
inherited)

The :DEFAULT-INITARGS class option is inherited; the set of initargs for 
a class that are defaulted is the union of the sets of initargs 
specified in :DEFAULT-INITARGS class options of the class and its
superclasses.  When more than one default value form is supplied for a
given initarg, the default value form supplied by the class that appears
earliest in the class precedence list is used.

Page 1-21:   Congruent Lambda-lists for All Methods of a Generic Function

Replace the old rules with the new ones.   

Probably should add a note about on 1-21 about this:

The proposal assumes CL-Cleanup issue KEYWORD-ARGUMENT-NAME-PACKAGE:ANY, which
stated that the names of &key arguments do not have to be keyword symbols.
The terminology of CLtL is used for discussing keyword arguments, but it
should be understood that keyword names are not necessarily symbols in
the keyword package; that's just a default convention.

Page 2-15 (and onward)  DEFCLASS section

Add :INITARG and :DEFAULT-INITARGS to the syntax diagram.

Add this info: 

The :INITARG -name- slot-option -declares- an initarg named -name- and
-specifies- that this initarg initializes the slot to which the
slot-option is attached.  -name- is any symbol.  If the initarg has a
value, the value is stored into the slot and the slot's :INITFORM, if
any, is -not- evaluated.  If no initarg specified to initialize a given
slot has a value, then the slot is initialized according to the
:INITFORM (if any).  This slot option can appear any number of times.

The :DEFAULT-INITARGS option is followed by a list of alternating
initarg names and default-initarg forms.  If one of these initargs does
not appear in the initarg list supplied to MAKE-INSTANCE, the
corresponding default-initarg form is evaluated, then the initarg name
and the form's value are added to the end of the initarg list.  The form
is evaluated every time it is used.  The lexical environment in which
this form is evaluated is the lexical environment in which DEFCLASS was
evaluated.  The dynamic environment is the dynamic environment in which
MAKE-INSTANCE was called.  The :DEFAULT-INITARGS option may be specified
more than once.  However, an error is signaled if an initarg name
appears more than once in a single :DEFAULT-INITARGS option, or in more
than one :DEFAULT-INITARGS option for a single class.

>>>> Add descriptions of each of these functions to Chapter 2: 

(MAKE-INSTANCE -class- &key -initargs-...) => -instance-

Users call this function to create objects.  Class can be either a class or
the name of a class.  Meta-users can define new methods for MAKE-INSTANCE
to replace the object-creation protocol.  For details, see
the section "Object Creation and Initialization".

(INITIALIZE-INSTANCE instance &key &allow-other-keys)

MAKE-INSTANCE calls this with the freshly-created instance, any initargs that
were supplied to MAKE-INSTANCE, and any defaulted initargs.  Users define
methods for this to create method-implemented initargs.  Typically,
user-defined methods are :AFTER methods, however that is not a requirement.

The primary method for INITIALIZE-INSTANCE is system-supplied and takes care
of the slot-filling initargs.  For each slot (whether local or shared):

  - if an initarg was specified or defaulted that fills that slot, its
    value is stored into the slot.  (This is true even if a :BEFORE method
    has modified the slot.)

  - otherwise, if the slot is uninitialized and it has an initform, the
    initform is evaluated and the result is stored into the slot.

  - the duplicate-resolution rules mentioned in the section "Rules for
    Duplication of Initargs" are obeyed.

Implementations are permitted to optimize initforms that neither
produce nor depend on side-effects, by evaluating them and storing them
into slots before running any INITIALIZE-INSTANCE methods, rather than
handling them in the primary INITIALIZE-INSTANCE method.  (This might be
implemented by having the ALLOCATE-INSTANCE method copy a prototype
instance.)  

Implementations are permitted to optimize default value forms for 
slot-filling initargs by not actually consing the complete initarg list,
when the only method that would see the complete list is the 
system-supplied primary method, e.g. when no other methods use &REST.
In this case default value forms can be treated like initforms.  This
has no visible effects other than a performance improvement.

(SLOT-BOUNDP instance slot-name) => boolean

Allows writing INITIALIZE-INSTANCE :AFTER methods that only initialize slots if
they haven't been initialized already.

(SLOT-MAKUNBOUND instance slot-name) => instance

Restores a slot to the uninitialized condition.


>>>> Add descriptions of each of these functions to Chapter 3

>>> Functions underlying the tools

It is undefined what happens if you modify the values returned by any
of the functions in this section.  It is permitted, but not required,
for an implementation to return values that share with internal data
structures.  Some of these functions will be SETF'able; which ones
remain to be determined.

(CLASS-ALL-INITARGS class) => list of initarg names, including inherited
ones.  This is (REDUCE #'UNION (MAPCAR #'CLASS-DIRECT-INITARGS cpl)).

(CLASS-DIRECT-INITARGS class) => list of initarg names.  This works by
computing the applicable methods for ALLOCATE-INSTANCE and for
INITIALIZE-INSTANCE and examining their lambda-lists (using
METHOD-KEYWORD-NAMES), then combining that with the class's list of
slot-filling initargs.

(CLASS-ALL-INITARG-DEFAULTS class)
   => ((initarg-name default-value-function default-value-form)...)

(CLASS-DIRECT-INITARG-DEFAULTS class)
   => ((initarg-name default-value-function default-value-form)...)
This reflects the :DEFAULT-INITARGS option.  Default-value-form is the form
that was originally specified, and is retained purely for explanatory
purposes.  default-value-function is what gets actually called; its effect is
equivalent to enclosing default-value-form in the appropriate lexical
environment.  Default-value-function takes no arguments.

(CLASS-ALL-SLOT-INITARGS class) => ((initarg-name slot-name...)...)

(CLASS-DIRECT-SLOT-INITARGS class) => ((initarg-name slot-name...)...)
This reflects the :INITARG slot-option.

(COMPUTE-APPLICABLE-METHODS generic argument-list) => list of methods

(METHOD-KEYWORD-NAMES method) => list of symbols or &ALLOW-OTHER-KEYS,
indicating the keyword names of the keyword parameter specifiers in
the method's lambda-list.  The result is the symbol &ALLOW-OTHER-KEYS
instead of a list if the method's lambda-list contains that symbol.


>>> Meta-object functions

(ALLOCATE-INSTANCE class &key &allow-other-keys) => instance

Meta-users can replace the system-supplied, implementation-dependent method
for this.  Any keyword arguments accepted by applicable ALLOCATE-INSTANCE
methods become valid initargs.

(CHECK-INITARGS class initarg-list)

Meta-users could replace the system-supplied method that implements the
normal rules for initarg validity.

(DEFAULT-INITARGS class initarg-list) => initarg-list

The system-supplied method implements the :DEFAULT-INITARGS class option
by appending initargs that do not appear in initarg-list to the end
of the returned list.  The initarg-list supplied as an argument is not
modified.  The order of initargs appended to the list is determined by
the duplicate-initarg rules listed earlier.

(FINALIZE-INHERITANCE class &key slots methods initargs)

This is called by the system at least once before a class is instantiated, and
is called again whenever anything relevant changes.  System-supplied methods
for this conspire with methods for CHECK-INITARGS, etc., to make MAKE-INSTANCE
faster.  Users with their own optimization needs can add methods for this
generic function that will precompute things based on inherited information,
and update the precomputed information whenever anything changes.

The :slots, :methods, and :initargs arguments are booleans that are true
when the specified type of inheritance needs to be recomputed.