On 12/12/06, TSa <[EMAIL PROTECTED]> wrote:

Jonathan Lang wrote:
> In any case, one should never add
> anything while going from specific to general.

The crux of my example is that one indeed adds methods
in the supertype. The subtype receives a standard
implementation. This pattern is applicable whenever
you have a case like the Num that can be embedded in
the Complex with .im == 0. Another example is deriving
a Point3D from a Point2D by giving a default
implementation of .z == 0.

I'd tend to agree.  This is an important feature of an object system
for me, if only because it allows the Num => Complex transformation,
which is one that has always bugged me (more specifically, if the
language gives you real numbers but not complex numbers, how do you
integrate complex numbers in a seamless way; i.e. such that the reals
are in fact a subset of the complexes).

There is a nice duality in this specification. When you say:

   role Foo does Bar does Baz {...}

You are saying Foo = Bar (*) Baz (*) ...;  # intersection of Bar, Baz,
and the following spec

When you say:

   role Foo superdoes Bar superdoes Baz {...}

You are saying Foo = Bar (+) Baz (+) ...;  # union of Bar, Baz, and
the following spec

It may seem like that should be Foo = (Bar (+) Baz) (*) ...;  (indeed,
I thought that for a while and then corrected myself).  But if
anything that is a Bar is a Foo, anything that is a Baz is a Foo, and
some other stuff is also a Foo (this being the important case), then
that should clearly be a union.

Things work a little differently for required methods.  When a
superrole requires a method be implemented, we (the language
designers) have a choise to make: it is illegal if the superrole
requires a method that the subroles don't implement or don't
themselves require, or it simply adds the new method to the required
list of the subroles which don't implement it.  I'm inclined to say
the former, even though it seems a little more brittle.  It means that
when you are implementing a role in a class, all you have to look at
for what you need to do is the role and the roles that it includes;
you don't need to scan the entire source for superroles which
encompass the one you are implementing.  That is the user-friendly way
to put it, but there is an analogous argument for independent
modularity (you don't want the definition of a new superrole to
invalidate objects that already exist).

Things get a little tricky when you think about "does" and "superdoes" together:

   role Foo does Bar superdoes Baz {...}

What does that mean?  Okay, Foo is a subset of Bar, and a superset of
Baz.  Already by simply declaring this we have mandated that "Baz does
Bar".  If Baz doesn't already conform to the interface of Bar, Foo
better fill in the gaps for it.  As for the ..., anything which
conforms to it and Bar ought to be in Foo.  So I think the equation
ends up:

   Foo = Bar (*) (Baz (+) ...);

In words, you are a Foo if you are a Bar and you either are a Baz or
conform to my interface.

You can get into some yucky grouping issues because union and
intersection do not commute with each other, but I think the rule for
determining the equation that seems "natural" is the following:  if
your declaration looks like "role Foo does B1 does B2 ... does Bn
superdoes D1 superdoes D2 ... superdoes Dm { SPEC }", then the
equation will be:

   Foo = B1 (*) B2 (*) ... (*) Bn (*) (D1 (+) D2 (+) ... (+) Dm (+) SPEC)

That is, you have to do all the Bs, and you may either do any of the
Ds or do my interface.  Keep in mind that all the Di will conform to
"my interface" after this declaration anyway.

Whew, I was really only going to reply with an "I dig it" sort of
response, but I seem to have written some sort of complete theory. :-)

Exercise for the reader: generalize to subtypes, delegation, and inheritance.


Reply via email to