With regard to the 'square' 'isa' 'rectangle' or vice versa question, surely it is for the programmer to decide depending on the situation. Though the problem is how to define criteria for trapping problems at compile time.

Regarding classes and roles, and in fact multiple inheritance in general, I am still unclear. For my own understanding, I have tried to analyse various situations using the idea of sets and subsets and Venn diagrams for demonstrating the relations between sets and subsets (please forgive the icky character graphics).

case 1 (Class B is a subset of class A):
-----------------------------------------------------------------------------------
| Class A | | -------------------------------------- | | | Class B | | | -------------------------------------- | ----------------------------------------------------------------------------------

My understanding of inheritance in other languages:
Class A 'isa' Class B, and inherits all of the attributes and functionality of Class B, and extends functionality and attributes.

It is also possible for Class B to be ('isa') Class A, and ignore the extra functionality of A. Though why one would want to do this is unclear.

My suggested interpretation of roles:
Class B is a role, and Class A is built from Class B, extending its functionality.

case 2 (Class A and Class B intersect):
--------------------------------------------------------------
| Class A                                               |
| ------------------------------------|---------------------
|                        | Class A U Class B     |                    |
| | | | -------------------------|----------------------------------- | | Class B | ----------------------------------------------------------

Usual OO technique:
This Class B inherits the functionality of Class A, but then over-rides (anuls) some of the functionality of A. Especially, if functionality in B is named in the same way as in A.

Question: Is this the sort of behaviour that is forbidden in some languages.

Role-playing programming:
For the sake of programming sanity / ease of debugging, both Classes A & B are built from a role that represents their intersection ( Class A U Class B), and then code is added in the definitions of the classes to extend the functionality - possibly using over-riding with same-name methods/attributes for different types.

case 3 (multiple subsets):

-----------------------------------------------------------------------------------
| Class A | | -------------------------------------- | | | Class B | | | -------------------------------------- | | -------------------------------------- | | | Class C | | | -------------------------------------- | | -------------------------------------- | | | Class D | | | -------------------------------------- | ----------------------------------------------------------------------------------

This would require multiple inheritance.

In perl6 (I think), Class A would be built from the 'roles' of classes B-D.

But the question is what would 'ref' return? Would it only define a match for class A, or would it recognise the existence of Classes B-D? If so, how?

case 4 (multiple intersecting sets - only four shown):
-----------------------------
-------------------------------------------------|------------  Class D  |
| Class A                                   |           |                |
| ------------------------|-----------|-----------------|-------- | | ------------|----------------- | | --------------|------------- | | -----------|-------------|-------------|--------------------- | | | | Class B | | --------------|------------------------------------------------
          |    Class C          |
          ----------------------------

I dont know how other OO languages handle this.

But with roles, each of the intersections could be built from roles, and then the classes from the roles.

Regards,
Richard

Luke Palmer wrote:
On 8/25/06, Daniel Hulme <[EMAIL PROTECTED]> wrote:
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.

This is the well-known Circle/Ellipse problem, and it relates to the
theory of value types.

We'll first take the traditional conception that a Square is a
Rectangle, the opposite of your viewpoint.  This is based on the
geometrical definitions of these shapes.  This makes sense from a
usage standpoint:

   sub rect_perimiter(Rectangle $x) {
       2*$x.width + 2*$x.height;
   }
   sub square_perimiter(Square $x) {
       4*$x.width;
   }

It makes sense to pass a Square to rect_perimiter, but not to pass a
Rectangle to square_perimiter.  That is one indication that Square is
a subtype of Rectangle.

However, say that Rectangle had set_height() and set_width() methods.
You cannot set the height and width of a square independently, so a
function like this:

   sub make_dims(Rectangle $r, $w, $h) {
       $r.set_width($w);
       $r.set_height($h);
       POST { $r.area == $w * $h }
   }

Must fail if $w and $h are not equal (and depending on Square's
implementation, might fail if they are equal).  You could say that the
failure is on the implementation side: we couldn't implement set_width
and set_height appropriately.

So clearly a Square is not a Rectangle.

Let's look at the other way around (your viewpoint):  a Rectangle is a
Square.  This makes sense from an implementation point of view, as you
point out.  We just take Square's methods and add a couple of
capabilites.

However:

   sub area(Square $x) {
       $x.width ** 2;
   }

Fails (by returning the wrong thing, worse than dying) if you pass it
a Rectangle.  So it failed from the usage point of view.  You could
say "that should be a method", but then you should say that everything
that uses a square should be a method of square, because we must be
able to make assumptions about the  behavior of classes.  If you say
that my sub makes an assumption that it shouldn't, realize that the
only thing it is assuming is that a Square is a geometrical square.
If I can't make that assumption, then Square is not a very good name
for that class.

So clearly a Rectangle is not a Square.

One way failed on the implementation side, the other on the usage
side.  I'd argue that the "Rectangle is a Square" view's validity can
only be argued from an implementation laziness point of view (which is
okay, but it must also be balanced with other issues).

The "Square is a Rectangle" point of view only works if they are value
types: if you are not allowed to modify anything.  In that
circumstance, it works very cleanly[1].  But if you are allowed to
modify things, then the two classes must be siblings, not parent-child
related.

Luke

Reply via email to