Gordon,

On Oct 11, 2005, at 9:10 PM, Gordon Henriksen wrote:
On Tue, Oct 11, 2005 at 06:10:41PM -0400, Stevan Little wrote:
I would like to propose that class methods do not get inherited along
normal class lines.

You mean, make them *not methods?* Because it's not a method unless it
has an invocant, as far as I'm concerned. (Method implies polymorphism.)

No, they would still have an invocant. That invocant would be an anon- class which itself is an instance of Class. It works like so:

(First, lets make a legend)

  --> is instance of
  ..> is subclass of

NOTE: Class means the class named "Class", this distinction is important.

When you create the class Foo, this is what you have:

  Class
    ^
    |
   Foo

Foo is an instance of class Class (Class itself is an instance of class Class too, but thats only slightly relevant here).

When you add a class method (one which cannot be inherited), it is done with an eigenclass. This changes the above structure into this:

  Class
    ^
    :
  eFoo
    ^
    |
   Foo

Now, we have created an anon-class (or eigenclass), whose entire purpose is to hold the class methods of Foo. Since the eigenclass is a subclass of Class, then all of Class's methods are inherited. This means that our method dispatcher does not need to know about class methods as a special case, as far as it is concerned, they are just normal instance methods on Foo (which itself is an instance of eFoo, which is then a subclass of Class).

Now, why are they not inherited. Lets expand this diagram a little more:

        Class
          ^
    +-----|----+
    |          |
   Foo<.......Bar

So Foo and Bar are both instances of Class, and Bar is a subclass of Foo. It is fairly straightforward, but now lets introduce the eigenclasses to hold class methods.

        Class
          ^
    ......:.....
    :          :
  eFoo        eBar
    ^          ^
    |          |
   Foo<.......Bar

Now, method dispatch for Foo will go to eFoo (since Foo is an instance of eFoo, and method dispatch always starts at the class-of the instance), and it will continue up to Class (since Class is the superclass of eFoo). The same goes for Bar, first to eBar, then to Class. Since eFoo and eBar are not connected, then normal method dispatching does not go along those lines.

Now, this is not to say that it cannot be made to do so. A slight change to the above diagram allows for inheritence of "class methods" very easily.

  Class
    ^
    :
  eFoo<.......eBar
    ^          ^
    |          |
   Foo<.......Bar

Now, method dispatch for Bar will go first to it's class (eBar), then to any superclasses (eFoo), and any of their superclasses (Class), and so on, and so forth. A better diagram of this can be found here (http://svn.openfoundry.org/pugs/perl5/Perl6-MetaModel/docs/ Method_Dispatch_w_EigenClasses.jpg).


: Let's start by making a very basic definition of an *object*,
: ignoring any implementation details or specifics.
:
:   object == state + behavior

I don't see how this is a bad thing. Classes don't have state. That's
their lot in life. Que sera! Inheritance of behavior alone is useful.

Classes do have state though. They have class attributes:

class Foo {
    our $.bar;
    my $.baz;
}

that state is just not inherited.

I am not actually arguing that inheritance of just behavior is not useful, more that inheritance of behavior *without the accompanying state* is not useful, and in many cases wrong.


My primary want for class methods, as a whole, is to provide this sort
of interface in Objective-C:

    @interface Host : NSObject {
    - (Class)plugInClass;
    - (void)setPlugInClass:(Class) plugInClass;
    }

    @interface PlugIn : NSObject {
    - (BOOL)initWithHost:(Host *)host;
    + (BOOL)supportsFeatureA;
    + (BOOL)supportsFeatureB;
    }

    ... later ...
    if ([[host plugInClass] supportsFeatureA]) {
        ... expose UI element ...
    }

My Objective-C is very rusty, but let met see if I understand what you are doing.

Host has-a Class object, which it uses as a plugInClass. Your PlugIn then has class methods (supportsFeatureA, supportsFeatureB) which can be used by the Host to query the capabilities of its plugInClass.

This type of thing could be accomplished with Roles.

class Host {
    my $.plugInClass;
}

role PlugIn {
    method initWithHost (Host $h:) { ... }
}

role SupportsFeatureA {
    # yes, this Role has a "class method" in it, which
    # the consuming class will get as a "class method"
    method supportsFeatureA (Class $c:) { bool::true }
}

role SupportsFeatureB {
    method supportsFeatureB (Class $c:) { bool::true }
}

class AB {
    does PlugIn;
    does SupportsFeatureA;
    does SupportsFeatureB;
}

One could argue that it is more typing, however, I think that in the long run, it will be less typing since you never need to repeat the supportsFeatureA or supportsFeatureB method, just consume the appropriate Role. As for subclassing of PlugIn's, that does get a little trickier, however if Roles and Classes are interchangable (which is not really specced that way in A12, but the current meta- model prototype does support), then we can do this:

role SupportsFeatureC {
    method supportsFeatureC (Class $c:) { bool::true }
}

class ABC {
    does AB;
    does SupportsFeatureC;
}

This basically consumes AB as if it was a Role, rather than subclassing it, and the resulting ABC class should just DWIM (I would really have to test this to be sure, but I think the idea is sound).

This system does not require inherited class methods, cheaply initialized PlugIn classes, or a Factory pattern. Using Roles and role composition I think we get an relatively simple and very flexible system.

Also, of course, inheriting constructors is nice. Of course, it opens up
the whole "designated constructor" can of worms. But that's okay; if
you're subclassing, it's your responsibility to make it work....

If you use the BUILD submethod, then you never need to worry about a that, everything is initialized for you by BUILDALL. Now, if you want to have a constructor which accepts positional arguments rather than named pairs (as the default does), then you have a valid need to override &new. Whether you should force this upon all your subclasses is a matter of opinion I think.

Stevan

Reply via email to