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