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

(long) CLOS Declaration Proposal Text

	CLOS Declaration Proposal

James Kempf	HP Labs		4/16/87


	The purpose of this proposal is to explore
issues regarding integration of the Common Lisp Object System
(CLOS) into the declaration mechanism in Chapter 9 of Steele.
Some specific "trial balloon" proposals will also be made 
for suggested additions, but, as usual, they are open for comment
and revision as agreement on the fundamentals emerges.

	There are several reasons why adding declaration
mechanisms for the CLOS to Common Lisp may be desirable:

1) It could tighten the integration between CLOS and the 
underlying language.

2) It could give application developers on conventional architectures
a means of achieving extra performance after their applications 
have been developed.

3) It could give vendors a means of freezing the semantics of
operations on objects with user defined classes, so users could
inherit from vendor delivered libraries of classes without having
the order of methods invoked during a CALL-NEXT-METHOD change
depending on the user's class structure. Additionally, if
the suite of method combinations could be frozen, a vendor could
deliver a class library as a "black box", in which the user's
options for modification of the semantics are limited.

Points 2  and 3 deal with aids for helping applications developers
move from a prototyping phase to a product or delivery phase,
with a minimum amount of recoding, beyond the addition of
(possibly local) declarations. Point 3 also would simplify
regression testing for class libraries, since developers could
more easily identify areas where user code may cause problems
and take steps to avoid them appropriately. (Footnote: An
example of this is the :PRINT-FUNCTION option to DEFSTRUCT).

	ANSI 87-002 (the CLOS specification) has a number
of areas where declarations play a role (most specifically,
for DEFGENERIC-OPTIONS). This proposal should be seen as
an effort to compliment these areas, and to tie them
together with the rest of Common Lisp.

	Note that this proposal will *not* discuss the issue of
compilation semantics for the CLOS (though, perhaps, it should, since
extra semantics are being proposed). This is also an area in
need of clarification, but complex enough to require a seperate

	There are a number of areas where potential optimizations
could be applied to CLOS code:

1) Where the exact class of a variable is known at compile time, 
method dispatch at run time can be avoided (or the amount of
time involved significantly reduced).

2) If the inheritance structure of a class is frozen, slot
access could be compiled in-line into a simple memory reference.

3) Lack of class redefinition could also allow double indirection
for slot reference to be avoided, potentially saving memory
(though this becomes less important as memory gets cheaper)
and cycles.

4) If the class of a lexical variable is known, and the compiler
can infer that the object reference will not be passed
outside of the lexical scope, then the compiler may allocate the
instance on the stack. This could encourage developers to use
instances in loops and in other areas where excessive garbage
creation may otherwise be a concern.

5) If method combination is prohibited for a certain method,
any overhead which is needed to support method combination
in the general case could be avoided.

	Note that some CLOS implementors may choose to include
these optimizations as part of general compiler optimizations
done when an OPTIMIZE declaration is in force. However, in certain
cases, additional information is needed from the application
developer in order for the optimizations to be made.

	In the rest of this note, suggestions for additions
to Steele, Chapter 9 to support Points 1-4 will be discussed.
Though Point 5 seems like an important area where developers
may require additional language mechanisms, I do not feel 
knowledgable enough about the implementation of method combination
to make a suggestion, and will leave this point open to comment
from method combination bgwahns (= supergurus). It may even be a 


	With reference to the list of potential optimizations above,
here are some areas where specific declarations may be desirable.

A. Declaration of Class

Common Lisp currently has no mechanism for saying that this variable
will always be bound to an object of *a* particular type, and no other.
The TYPE declaration always allows subtyping. If a user defined class
has a type of the same name (Footnote: I couldn't find a statement to this
effect in ANSI 87-002, so I'm guessing) and subtyping and subclassing
are synonomous (i.e., if X is a subclass of Y, then it is also a
subtype), then the TYPE declaration can be used to restrict a variable's
class to a particular part of the class hierarchy, which could potentially
be useful in optimization.

However, additional mechanism may be needed to further restrict a variable's
reference to objects of one (and only one) class. A suggestion for this 
purpose would be a CLASS declaration:


would restrict X to objects of class FOO, and no other. This would allow
very efficient compile time optimization of method lookup. Essentially,
a reference to the function implementing the method could be inserted
at the call site.

Note that a declaration of this sort would severely restrict flexibility
(resembling *shudder* Pascal static typing) but could be useful in
postdevelopment, when the developer has discovered that certain variables
only get bound to objects of a particular class. In time critical 
sections of code, developers might arrange to restrict the class
of objects. Declarations of certain variables to one of the built-in
Common Lisp type classes may allow substitution of a Common Lisp function 
directly. Finally, even if the class of the primary argument varies,
the classes of other arguments in a multimethod dispatch could be
dynamically nonvarying for particular invocations.

B. Declaration of Function Class or Type

Currently, Common Lisp has two ways of declaring the argument and
return type of a function (FTYPE and FUNCTION pg. 158-159 of CLtL). 
The existing mechanism may be adequate for specifying the same 
information about generic functions as well, with the exception 
of the lack of ability to restrict an argument type to a particular 
class (and no other), as noted above. The addition of CLASS to
the declaration may achieve this:


would specify that FOO takes objects of class BAZ (and *only* of
class BAZ) and returns objects of class BOO (and *only* of
class BOO).

Alternatively, a user may want to restrict a particular name to
be a generic function, and so a parallel GENERIC-FUNCTION and
GENERIC-FTYPE declaration (rather than a function) may be desired. 
In the interest of parsimony (there are, after all, already 
*two* ways of achieving similar functionality) perhaps the 
declarations for function should apply to generic functions, 
and a specific GENERIC-FUNCTION declaration be introduced for 
declaring that a particular name is for a generic function:


Note that the issue of lexical generic function definition 
(GFLET and GFLABELS) has not yet been resolved, but, should
it be, then the same rules should apply for generic
function declaration naming as for functions.


The meaning of these declarations for generic functions should
be carefully thought out. In particular, if an implementor
chooses to implement INLINE, then does the generic function body
itself get inserted inline? This would seem to be the case,
unless further optimization of method lookup is possible.


Compiler writers should be left a good deal of freedom to
implement CLOS code optimizations as they see fit, though
they should be encouraged to document what optimizations
are done in the CLOS for particular levels. The information
in the other declarations could be used to good effect


It seems as if declarations for metaclasses may also be
needed, but specific proposals should probably wait
until the metaobject protocol is accepted.


	The term "staticization" (introduced by Dick Gabrial)
referrs to making a portion of a class hierarchy static. 
This could involve a number of potential effects:

1) Redefinition of the class is no longer allowed.

2) Slot layout is fixed.

3) WITH-SLOTS compiles out any dynamic lookup.

4) All included classes in the hierarchy need to
be staticized as well.

5) User defined classes should be able to inherit from
a staticized class.

6) The class precedence list is frozen.

The overall idea is to move resolution of as much inheritance
compuation as possiblefrom run time to compile time, without 
changing the inheritance semantics. Note that semantic changes 
*may*, in fact, become visible to subclasses of a staticized
class if method inheritance is resolved at compile time 
(as noted in the introduction). The order in which CALL-NEXT-METHOD 
invokes a method and the class precedence list order could be 
frozen for inheriting classes, meaning that the semantics of method
inheritance would not change as the user inherits classes for
multiply inherited classes. If the class precedence list is NOT
frozen, then the semantics will change, depending on the order
in which the user lists the superclasses in the :INCLUDE form.

The issues here are complex, and need more discussion. Perhaps
several levels of staticization are desirable, as is the
case with SPEED and SAFETY. At one level, the class precedence
list would not be frozen, but redefinition would be excluded.
At the next, slot layout would be frozen, and finally, the
class precedence list would be frozen as well.

In any event, the nature of the CLOS, with classes being
first class objects, suggests that a (generic) functional 
interface rather than a declaration be used. As a starting point,
I would suggest MAKE-STATIC with an ESSENTIAL-CLASS argument.
Keyword and optional arguments to specify particular staticization
level may be needed, if the additional complexity of such
levels seems desirable. A MAKE-STATIC method for ESSENTIAL-METHOD
may also be needed. Perhaps these should be included in the
metaobject protocol.


As noted in the introduction, this note is meant to start up
(perhaps lively) discussion on the issues of optimization
and class staticization. Perhaps all of it should be junked!
But, in any event, I hope the issues don't get ignored, since
I feel confident that developers will want hooks into the 
declaration mechanism, and the ability to deliver class 
libraries that have been processed by postdevelopment 
mechanisms to make them as efficient and reliable as possible.