[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Date: 18 Sep 85 18:55 EDT
Why Automatic Shadowing of Slots Is Essential
Dave Touretzky, CMU
In this note I will try to explain why automatic shadowing of slots is
essential to a Common Lisp flavor system. I have read the ObjectLisp
document distributed at the IJCAI Common Lisp meeting in Los Angeles, which
I attended, but I am not otherwise familiar with the CommonLoops or HP
flavors proposals. I hope people who do understand these proposals will
respond to the points raised here.
Example 1: Customizing a Flavor. A user of a hairy window system wants to
create his own flavor of window, called PORTHOLE, which is like an ordinary
window in most respects. But PORTHOLE has an instance variable N which the
user can access and modify. He defines PORTHOLE as a subtype of WINDOW, and
writes methods GET-N and SET-N for accessing and setting N. Unbeknownst to
him, however, the window system is already using N as the name of a slot.
Suggested behavior: instances of PORTHOLE should have two slots named N,
and one of them, WINDOW's N, should be shadowed for PORTHOLE's methods.
Let P1 be an instance of PORTHOLE. Sending P1 a GET-N message should access
the N the user intended: PORTHOLE's N. Sending P1 a CLEAR-SCREEN message
should access the N inherited from WINDOW, since the CLEAR-SCREEN method is
itself inherited from WINDOW and the user knows nothing about the shadowing
of WINDOW's N by his own use of the name.
Packages solve this.
Example 2: Replacing a Method. Suppose a hacker who is familiar with the
window system wants to create his own type of PORTHOLE, HACKPORT, that uses
a different CLEAR-SCREEN method. Since HACKPORT is an instance of PORTHOLE,
references to N by a HACKPORT method will normally be interpreted as
references to PORTHOLE's N rather than WINDOW's N. So we need a way to
reference the shadowed N in methods defined for PORTHOLE or HACKPORT. I
won't suggest a notation for this here. However, the next example argues
that we must reference shadowed slots by a point on their inheritance path,
e.g. by saying PORTHOLE's N; we can't just say something like SHADOWED-N
and expect the reference to be resolved.
Packages provide a suitable notation.
Example 3: Combining Orthogonal Flavors. A naive user has available
to him two predefined flavors, WINDOW and STACK, about whose internals
he knows nothing. He wants to build a flavor called VISIBLE-STACK,
which is a stack whose contents are constantly displayed in a window.
He defines VISIBLE-STACK as a subtype of both WINDOW and STACK, not
knowing that both these flavors contain instance variables named N.
Suggested behavior: Let S1 be an instance of VISIBLE-STACK. Sending
a POP-STACK message to S1 should access STACK's N, while sending a
SET-FONT message to S1 should access WINDOW's N.
Packages solve this.
Problem: let METHA be a method defined for VISIBLE-STACK. How should
references to N be interpreted inside METHA? If VISIBLE-STACK has its own
instance variable named N, then this N should shadow both WINDOW's N and
STACK's N. But if there is no N defined at the level of VISIBLE-STACK, then
references to N inside METHA should generate an error message: "N is an
ambiguous slot reference."
Packages solve this; a reference to N inside METHA specifies which N it means
explicitly. Is there a reason why this is inadequate? Packages already provide
for an error to be signalled if there is an attempt to inherit two symbols with
the same name.
Problem: how do we write a new CLEAR-SCREEN method for VISIBLE-STACK? We
will need notation to explicitly specify that we want to access the
shadowed WINDOW's N rather than the shadowed STACK's N.
Packages provide a suitable notation.
It is obvious what the ObjectLisp solution to this set of problems would
be: place each N in a different package to keep the names distinct. I find
this highly unsatisfactory. In the degenerate case, each flavor will end up
with its own associated package in which instance variables are defined. A
user of the ELEPHANT flavor will have to contend with slot names from a
host of packages, e.g. ELEPHANT:TRUNK, MAMMAL:HAIR-COLOR,
VERTEBRATE:SPINE-LENGTH, ANIMAL:HABITAT, LIVING-THING:LIFESPAN, etc.
I think this is somewhat of a straw man. Aren't these flavors all part of
the same software module (unlike the earlier examples), and hence all in the
same package and presumed to use non-clashing names?
course the packages could be organized into a hierarchy mimicing the flavor
hierarchy, so that ELEPHANT:HABITAT would reference ANIMAL:HABITAT unless
shadowed, but that wouldn't provide for detection of ambiguous slot
references as required in Example 3 above.
Packages already provide for an error to be signalled when there is an attempt
to inherit two symbols with the same name.
Furthermore, unless the
flavor/package duality was rigidly enforced by the flavor system, the user
could create quite a mess by hacking the package stuff by hand.
Finally, if packages are intended to be used for encapsulating separate
software modules, then we must allow each package to have its own set of
flavors defined within that package. Thus, in my hairy robot system, the
flavor ARM:STACK may describe a stack of blocks, while PLANNER:STACK may be
an entirely different type of flavor, such as a stack of goals. If flavors
usurp the package system for other tasks, like slot shadowing, the original
purpose of packages -- encapsulation of software modules -- will be hindered.
How are flavors different from software modules?
I'd like to see counterarguments, but I suspect that they will be of the form
"packages are no good in general" rather than of the form "packages are adequate
for resolving some name clashes, but not for resolving name clashes for