In a message dated Fri, 25 Aug 2006, Daniel Hulme writes:

If "changing that functionality beyond recognition" means changing its
external behavior (as opposed to its internal behavior) so that it
acts  differently from what the superclass had promised to do, then
no, it's not  any weirder--but I can't figure out how the contract
would work, either.

That's because you're used to one way of thinking about class
inheritance: that the subclass can do everything that the superclass can
do, and more. In this scheme, you might have a Square class, with a
field representing its corner and another giving its side length. Then,
you could build on this to have Rectangle as a subclass, which adds an
extra side length, and extra accessors for it. This is a really bad way
of making your subclasses work, but your Rectangle has all the fields
and methods of your Square, and some extra ones.

Mmmm, no... depending on usage, I'd most probably have Shape->Polygon->Regular->Square, and Shape->Polygon->Rectangle, and write a FourSided role (perhaps a subrole of the Sided role, or maybe Sided can be parameterized so that Sided<4> could work) to capture the code that can be shared between Squares and Rectangles. I'm saying that subclasses *must* extend the promised capabilities of their parent classes, but that extending capability shouldn't be the only determinant of when you extend and when you make a sibling or a role or a trait.

But yes, if you didn't want to have any branching in your inheritance pole (not tree), you'd have to make Square the superclass of Rectangle.

Another way of looking at it is that the Rectangle is the more
generalised one, so it should be the superclass. It has a corner and two
side lengths, and associated accessors. Now, your Square is a subclass
of this. A Square in this scheme isa Rectangle, with the constraint that
the two side lengths are always equal. (Never mind the storage
considerations: that's internal.)

The problem comes up when code tries to do this:

  sub stretchWidth (Rectangle $r, Rat $ratio) {
      PRE { $ratio > 0 }
      $r.w *= $ratio;
      POST { now($r.w) == before($r.w) * $ratio
             now($r.h) == before($r.h)}

The pre and postconditions make perfect sense in the context of a Rectangle--we need a positive stretch ratio, and only the width will be modified, not the height. But then:

  for any(@shapes) ~~ Rectangle -> $s {
      stretchWidth($s, 2);

Again, looks perfectly reasonable--but one of @shapes was a Square, which is also a Rectangle. Hit that one, POST fails, and boom! Either .w is going to fail for being unmodifyable, or .w and .h are both bindings to .side, and so stretchWidth's postcondition will fail.

Most languages use the first scheme of class inheritance, but some offer
the second. Perl 6, AFAICS, has the first for subclasses, but offers the
second with subtypes and where clauses. I don't believe I've previously
used a language that offered both, so I'm interested to see how this
conjunction of features will turn out.

Yep.  I'm excited.


Reply via email to