Loose type checking in E

Ka-Ping Yee ping@lfw.org
Sat, 24 Oct 1998 01:09:28 -0700 (PDT)


On Fri, 23 Oct 1998, Tyler Close wrote:
> 
> I think you're still missing the magnitude of the point. There aren't any
> other things made inconvenient by the lack of inheritance, quite the
> opposite actually.

That's the part i'm having trouble with.  What, then, of the problem
that inheritance was specifically created to solve?

That is, what do you do when someone has handed you the ability to
create a certain type of object with some useful behaviour, and you
just want to extend that behaviour a little?

Do you have to sit there and mindlessly write dozens of delegating
stubs just to add a method or two?  The inheritance mechanism in E
is effectively nothing but a macro to write those stubs for you.

Lead me, as you would lead a Java-head well-steeped in the virtues
of inheritance, through a procedure by which i can take a design
typically done with inheritance, and lose no convenience (as you
say) while moving to a design without inheritance, and you will
convince me.

                        *     *     *

I will choose a question that you have likely already answered so
that we can see how it is resolved in a real application.  Let's
suppose that you are building a widget toolkit and you want it to
provide several types of buttons.  They all look like rectangular
things that can have a raised or lowered relief and a label, but
you can put the rectangle and the label wherever you want.

Let's suppose you want at least two types: a push button (where
the button surrounds the label, and the button goes down only
momentarily when clicked) and a check button (where the button
is a small square beside the label, and toggles up and down when
clicked).

A typical implementation using inheritance would first define a
class, say, "Button", which contains the necessary behaviour for
drawing a rectangle in relief, perhaps in a specified colour, and
can display a label at a particular place, as well as knowledge
of the size of the widget and the value represented by the widget.

It would then subclass "Button" with a "PushButton" class that
sized the rectangle around the label, positioned the label, and
told the button to go down when the mouse went down, go up when
the mouse was released, and do the activation callback if the
mouse is released while over the button.

It would also subclass it with a "ToggleButton" class that sized
the rectangle to a small square, positioned the label on its right,
and told the button to alternate between being drawn up and down
each time the mouse was clicked and released over the button or
the text.

Now, without inheritance these two classes would have to forward
the normal button-drawing calls to the Button methods, which
could be tedious and error-prone.  How did you do this sort of
thing without inheritance?

                        *     *     *

But the more serious issue is the upgrade problem: what happens
if now i want all my buttons to draw their labels in a different
font?  Now, i didn't foresee this improvement when i wrote Button.

If i had inheritance, i would only have to add one method (say,
'setFont') to the Button, and all my various buttons would
immediately benefit from the new functionality.

But if i did not have inheritance, i would not have known ahead
of time to delegate a 'setFont' call on all my types of buttons,
so now i have to not only add the functionality to Button but i
have to go find all my classes that delegate to Button and add
another delegating stub.

This becomes, of course, much worse if there is a larger tree
of delegations and different people maintain different classes.
By the time you get to the bottom (the leaves) of the tree, you
don't know who remembered or forgot to delegate what.  Hence
you no longer have the guarantee, which inheritance gives you,
that everything underneath responds to the complete interface
provided by what's above -- and that makes upgrading trickier.

                        *     *     *

> It is informative to note that only two of the Design Patterns presented in
> Erich Gamma's book use inheritance. For these two patterns, Factory Method
> and Template Method, there are better alternative patterns / implementations.

Please, elaborate.  I'm curious.

                        *     *     *

> >On the other hand, of course,
> >even if you have inheritance, you can always choose not to use it.
> 
> So, if we keep inheritance, and people use it when they are writing code
> that they consider less important, do you think they will stop using it
> when they go to write code that they consider more important?

As you suspect, probably not -- unless there is a sufficiently
strong external reason to avoid inheritance (e.g. a general principle
that inheritance is bad for security).

But let me point out the flipside: inheritance is not something
you *can* entirely prevent in E.  Even without any macro sugar for
inheritance, people can write makers that will set up delegation
chains and type objects that will respond to queries about what is
derived from what.  The complementary question to ask, then, is
whether *not* including any endorsed way of doing lightly-used
single inheritance will cause several home-grown and different
kludges for emulating inheritance to spring up and compete with
each other.  (They might even define their own macros and get
them wrong...)



!ping


P. S. By the way, don't get me wrong: i'm all for simplicity.
      This position is written at least partly as a devil's
      advocate to make sure the issue is fully explored.