I accidently sent this directly to Richard.  Sorry about that, folks...

---------- Forwarded message ----------
From: Jonathan Lang <[EMAIL PROTECTED]>
Date: Aug 29, 2006 1:24 PM
Subject: Re: Classes / roles as sets / subsets
To: Richard Hainsworth <[EMAIL PROTECTED]>


Richard Hainsworth wrote:
I find classes and roles, and multiple inheritance in general, difficult
to understand. Larry Wall talked about subsets, so 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. The
idea is that the 'space' encompassed by a set in the diagram (labelled
as sets) is method functionality and attributes.

This may well miss the mark.  Some of the most important differences
between classes and roles come in when the sets of method namespaces
don't match up with the sets of method implementations - that is, A
and B both include a method m, but A's method m does something
different than B's method m.

See diagram Case 1 (Class B is a subset of class A):

Note that in Case 1, there isn't much difference between classes and roles.

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

So far, so good.  If you want A.m to do something different than B.m,
A redefines m.

It is also possible for Class B to be ('isa') Class A, and ignore the
extra functionality of A, presumably to create objects that are smaller
than the more general class.

Not according to clean OO programming theory.  If A isa B, then A will
carry with it all of B's internal mechanisms, even the ones that it
has closed off from public access.  If it fails to do this, it isn't
really a B.  Even when A redefines how m works, it includes an
implicit mechanism for getting at the original definition.  As such,
inheritance can only add to a class; it cannot remove or change
anything in any substantive manner.  The only point at which stuff
gets removed is when the code optimizer comes along and trims the fat.

Let's extend Case 1 by adding a Class C, which is a superset of Class
A.  C isa A, but A isa B; C can still access B.m, as well as A.m.
Think of A, B, and C as being layers that get added on top of each
other: A covers B, but doesn't actually replace it.

My suggested interpretation of roles:
Class B is a role, and Class A is built from Class B, extending its
functionality. A role without extra code becomes a class if it is
instantiated.

The motto for role A is "anything you can do, I can do too."  Note
that when role A declares that it does role B, it makes a promise
about _what_ it can do, but makes no promises about _how_ it will do
it.  In fact, roles don't even make a promise to supply any
implementation information at all: B doesn't have to say a word about
how m works - and for the purpose of composing roles, what it _does_
say about how m works should be taken not so much as a rule than as a
guideline.  As long as the composer has no reason _not_ to accept B's
version of m, it will; but at the first hint of trouble, B's version
of m will get jettisoned.

In Case 1, the only way that role B's version of m will get jettisoned
is if role A provides its own version.  However, A is not a B; it is
its own unique entity.  If A invalidates B's suggestion for how m
should work, then nothing that does A will implicitly have access to
B's version of m.  Unlike classes, roles can change from what they're
composed from, not just add to them.  However, because they don't
guarantee to tell you how anything works, they can't be used to
intantiate objects; that's what Classes are for.

See diagram case 2 (Class A and Class B intersect):

Usual OO technique:
Class B inherits the functionality of Class A, extends the functionality
(in one 'direction') but then over-rides
(anuls) some of the functionality of A (say, by redefining
methods/attributes used in A).

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

Very definitely, yes.  Removing capabilities during inheritance is a
no-no.  If you wanted Classes A abd B to be part of the same
inheritance tree, you'd have to define a common ancestor class that
contains the intersection of A and B, and then each of A and B inherit
from it.

As described, though, A and B are completely unrelated by inheritance;
each is a unique class which stands on its own.  The fact that both
contain a method m is a coincidence, nothing more.

This _is_ a useful case to consider, though, in terms of what happens
if you add a new class (C) which encompasses both A and B.  Assuming
that m is an element of both A and B, A.m doesn't neccessarily work
the same way as B.m; C.m needs to decide which version to use.
Traditionally, OO languages imnplement a system of seniority, where
one of the classes that C inherits from is given precedence over the
other - generally based on which one comes first in C's list of parent
classes.  The conflict between A.m and B.m thus gets resolved in favor
of the senior parent.  Of course, if C provides its own implementation
for m, that takes precedence - but A.m and B.m will still be there,
hiding under the surface.

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.

AFAIK, removing methods is the one thing that roles can't do, either.

With a role C that composes roles A and B, conflict resolution is
handled differently: if C doesn't provide its own mechanism for m, and
A and B provide potentially conflicting mechanisms for A.m and B.m
respectively, C simply leaves m without an implementation.  It's up to
whatever class does C to decide how m works.  And if that class wants
to use A.m, it can't tunnel into C in order to get at it; it will have
to explicitly do A as well, and then personally resolve any conflicts
that come up between A and C.

See diagram case 3 (multiple subsets):

This would require multiple inheritance.

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

Perl6 handles both object-orientation (through inheritance) and
role-playing (through composition).  Multiple inheritence is an OO
concept, and would work in much the same way as I described in Case 2.
But because B, C, and D don't overlap, the multiple inheritance of
Case 3 is comparatively trivial.

When dealing with the RP model, Role A composes roles B, C, and D.
Again, since none of them overlap with each other, there are no
conflicts to resolve.  Case 3 is actually closer to Case 1 in terms of
simplicity.

But the question is what would 'ref' (or perl6 equivalent) return? Would
it only define a match for (an object instantiated from) class A, or
would it recognise the existence of Classes B-D in that same object? If
so, how?

I _think_ that this answers your question:
Under the OO model: if x is an object instantiated from A, it can be
treated as if it were an object instantiated from B, C, or D as well.
Under the RP model: if x is an object instantiated from a class that
does A, it can be said that x does A; but it cannot be said that x
does B, C, or D.

So suppose an object OA is an instance of class A and and object OB is
an instance of class (role) B. Would object OA 'match' object OB in some
form, after all the functionality of OB is contained within OA?

Yes, by treating OA is if it were a B.

But then, this matching might not be transitive, viz. OA ~~ OB might be
true, but OB ~~ OA might be false because whilst OA contains the
functionality of OB, OB does not contain the functionality of OA.

Correct.

Again, this deals explicitly with the OO side of things.  On the RP
side of things, if OA is an instance of a class that does A and OB is
an instance of a class that does B, neither OA ~~ OB nor OB ~~ OA will
match.

See diagram case 4 (multiple intersecting sets):

This is "merely" a messier version of Case 2, with more than two
intersecting sets.  Everything that I said about Case 2 applies here
as well.

--
Jonathan "Dataweaver" Lang

Reply via email to