Brent,
On Oct 11, 2005, at 8:17 PM, Brent 'Dax' Royal-Gordon wrote:
Stevan Little <[EMAIL PROTECTED]> wrote:
I would like to propose that class methods do not get inherited along
normal class lines.
I think you're not thinking about many major usage cases for class
methods.
Actually I have considered many common usages including those which
you describe below, and it is my belief that only a few are truly
valid uses and not abuses of class method functionality. What I kept
coming back to was that pretty much all the *abuses* of class methods
were better done in some other way, and that even the *valid* uses
were nothing more than design choices, and could be accomplished in
some other manner.
For one example, look at my Cipher suite. (It's in Pugs's ext/Cipher
directory.) The Cipher base class implements most of the visible API,
while subclasses simply override a few internal methods; Cipher turns
the wide-ranging, convenient external API into a smaller, more easily
implementable internal API.
Your internal API and your external API have little to do with one
another as far as I can tell. The external API is simply a set of
convenience functions which create instances of your classes in
various ways (very cool ways I might add, especially the functional
API, very nice stuff). However, you could easily remove external API,
and your internal API would not really suffer, it would only require
that the user manually create what your class methods create for you.
While many people think Factories are many times overkill (me among
them), what you are doing is a perfect candidate for the Factory
pattern. In fact, one could say you are already doing an ad-hoc
Factory pattern with your inheritable class methods.
Some of Cipher's methods are class methods, including the
pseudo-procedural .encipher/.decipher and the pseudo-functional
.encipherer/.decipherer methods. These methods are included
specifically *to* be inherited.
Your documentation says the following things:
The Cipher API's procedural interface is good enough for many
purposes.
Although the interface is said to be procedural, it is invoked via
two class
methods.
The Cipher API is fundamentally object-oriented; the procedural
and functional
interfaces are layers on top of the object-oriented backend.
Both indicate to me an acknowledgment that you are knowingly abusing
the inheritance of class methods to make your functional and
procedural APIs work. Now, please don't take this as an insult or
slam of some kind. All good programmers know when to abuse language
elements to get what they need. However, I am of the opinion that
maybe we should leave these old idioms/abuses aside.
In my opinion, class method inheritance is an important part of
class-based OO--almost as important as object method inheritance.
I disagree with you on this point (of course, otherwise I would not
have started this thread), but I will admit that inheritable class
methods are a very common OO idiom, and that fact (good or bad)
should be taken into account.
Removing features simply because their implementation is inconvenient
is not The Perl Way. If it were, Perl 6 would be Java With Sigils.
To be honest, the implementation is not inconvenient at all, in fact
I have already done it twice (correctly at least, the meta-model
currently has inheritable class methods, but the implementation is
crap).
1) A Mini-MetaModel with Eigenclasses
http://svn.openfoundry.org/pugs/perl5/Perl6-MetaModel/docs/
MiniMetaModel_w_eigenclasses.pl
Whenever I am doing something which has the potential to dig deeply
into the core of the meta-model, I do it first with a Mini-MetaModel.
(The MMM (mini-meta-model) is a small self-bootstrapping single-
inheritance meta-model in under 2-300 LOC and usually which tends to
be much easier to "mess with" than the real meta-model.) If you look
at the "new" method in Class, you will see it creates an Eigenclass
for each class (this is where the class methods get stored), then
adding class methods is accomplished with the "add_singleton_method"
method. You will find a number of tests towards the bottom of the
file which demonstrate the inheritance of the class methods.
2) By using a subclass of Class
http://svn.openfoundry.org/pugs/perl5/Perl6-MetaModel/t/
37_inherited_class_methods.t
I did this test at autrijus's request, it creates a
"ClassWithInheritedClassMethods" class which is a subclass of Class.
If you create your classes (Foo, Bar, what have you) using
"ClassWithInheritedClassMethods", then you can add class methods,
again with the "add_singleton_method", and they are inherited
correctly by subclasses.
The code to make "ClassWithInheritedClassMethods" work is only 10
lines long, so as you can see the implementation is not difficult or
inconvenient at all.
To properly implement this in the current meta-model prototype would
not require all that much more effort than either of these examples.
Now, all this said, I think you make a very important point, which is
that such B&D does conflict with "The Perl Way". And that it probably
will get in the way of "getting the job done" which is just contrary
to what Perl is all about.
Stevan