From: frank@Cookie.secapl.com (Frank Adams)
Subject: Re: Const Inheritance
Date: Mon, 09 Nov 1992 16:29:57 GMT
Organization: Security APL, Inc.

In article <Bx9vsr.4ML@slipknot.rain.com> robert@slipknot.rain.com.UUCP (Robert Reed) writes:
>In article <1992Nov05.165658.34729@Cookie.secapl.com> frank@Cookie.secapl.com (Frank Adams) writes:
>|In article <BwwvMw.DDC@slipknot.rain.com> robert@slipknot.rain.com.UUCP (Robert Reed) writes:
>|>I have not violated the intended semantics, I have generalized them, within
>|>reasonable confines of the language.
>|
>|Since I'm giving the example, I know what the intended semantics are.  If I
>|am defining the Rectangle class in a context where I can make assertions, I
>|will assert that setWidth does not change the height, and vice versa.  This
>|is the intended semantics, even though the assertions are not available in
>|the language.
>
>  If C++ had declarative
>assertions, I would expect that placing an assertion on the width and height of
>a Square should not affect its base Rectangle class.

That isn't the constraint I was referring to.  I would put a constraint on
the setWidth function in the Rectangle class that it not change the height.
*This* is the assumption that will be violated.

>|void setDimensions(Rectangle& r, int x, int y) {
>|    r.setWidth(x);
>|    r.setHeight(y);  // corrected -- originally read setHeight
>|}
>|
>|I expect this function to work: after it is called, r.width() should be x,
>|and r.height() should be y.
>
>(I believe the 2nd setWidth() should be a setHeight().)   I see nothing in my
>counter-proposal which would deny the operation of this function.

But I can do the following:

    Square s;
    setDimensions(s, 2, 3);

This does not work; after the call, the width will not be 2.  (Of course,
presented this baldly, I would not expect it to work; but I could be passing
s to some other function which took a rectangle argument, and called
setDimensions on it; the resulting bug is quite subtle.)

>|>|>  Alternatively, you could make Rectangle a subclass of
>|>|>Square, define a setPrimaryDimension(int) for Square and add
>|>|>setSecondaryDimension(int) for the specialized Rectangle class.
>|
>|There is another problem with this, if I decide to generalize a bit: I may
>|want to define:
>|
>|    template<class WidthT, class HeightT>
>|    class Rectangle {...}
>|
>|    template<class SideT>
>|    class Square : public Rectangle<SideT, SideT> {...}
>|
>|In this case, Rectangle cannot inherit from Square; it would have to change
>|the types of the instance variables.  This is not an altogether artificial
>|example; I worked on a word processor (not written in an O.O. language) in
>|which horizontal and vertical distances were expressed in different units.
>|(This program had no use for squares, however.)
>
>If your intent is to use Squares on a system with different horizontal and
>vertical dimensions, then even your scheme of Const Inheritance doesn't solve
>the problem.  You still have to deal with how to specify a square.  Is it
>expressed in horizontal or vertical units?  Or both?  If you're going to
>constrain the problem so as to exclude alternate solutions, make sure your own
>solution still works.

Sigh.  Do I have to explain every little detail?  If I were simply using
different dimensions for width and height, I wouldn't have bothered making a
template.  The point of the template is that I may be using different types
for the dimensions in different places.  For example, I might be using
character widths and heights in one place, and points in another.  I would
not be able to use Square in the first context, but it would be quite usable
in the other.

Even more importantly, this is the kind of problem you run into when you
violate the "isA" relationship in you class hierarchy -- the resulting
program is fragile.  One of the chief advantages of OO programming is that
later enhancements can be done easily.  This kind of fragility makes that
much more problematic.

>My claim is still that this a brutish approach to a problem that has been
>solved in many other languages, such as Modula-3, Eiffel, etc.; that is,
>selective inheritance.  As such, const inheritance is a partial feature.  In
>the example we have been abusing, all you really want to restrict is CERTAIN
>geometry modifying functions--not even all of them.  For example, shouldn't I
>be able to define a Rectangle::setScale() function which could be inherited by
>Square?  It would change variables but not affect the geometric constraints
>you've expressed concern about.  How about a Rectangle::setBoundaryColor()
>function?  This is a nongeometric function and so should be equally usable by
>Rectangles and Squares.
>
>My problem with your proposal is not that it would not provide some useful
>extention to current syntax.  My concern is that your solution only covers a
>small subset of the problem domain, and as such must be considered a partial
>feature, a potential source of more frustration than facility.

Selective inheritance might be a useful feature to add to the language.  But
I see const inheritance as a completion of a feature already in the
language, not as an entirely new feature.