Re: txt vs OO [was: Re: Proposal to make class method non-inheritable]
On Tue, 25 Oct 2005, Larry Wall wrote: But we're trying to design the OO features (indeed, all of Perl 6) such that you can usefully cargo cult those aspects that are of immediate interest without being forced to learn the whole thing. It's not the number one design goal, but it's right up there. So you're talking about a positive, tame form of cargo cult, giving the latter a good name... Michele -- you need a brain hack. or a brain of any sort. try a nematode first. the small incremental improvement won't be such a shock. then you can graduate to segmented worm brains. - Uri Guttman in clpmisc, Array Sort Using Regex Matching Fails
Re: txt vs OO [was: Re: Proposal to make class method non-inheritable]
On Wed, Oct 26, 2005 at 09:36:48AM +0200, Michele Dondi wrote: : On Tue, 25 Oct 2005, Larry Wall wrote: : : But we're trying to design the OO features (indeed, all of Perl 6) : such that you can usefully cargo cult those aspects that are of : immediate interest without being forced to learn the whole thing. : It's not the number one design goal, but it's right up there. : : So you're talking about a positive, tame form of cargo cult, giving the : latter a good name... Yes, I hack on more languages than just Perl. :-) I use the term cargo cult for lack of a better term, and when I do so I tend to use it as a verb rather than a noun. By cargo culting I mean the process by which most of us learn to use new technology. We appropriate inappropriately; we have to start using a thing wrongly before we can start using it rightly. When we're very young, we learn by picking up objects and stuffing them into our mouths. That's not the appropriate way for an adult to treat most objects, but it's the right way for a toddler to treat an object, and it's the parents' responsibility to child-proof the room so the child doesn't chew on power cords and such. So another way to say what I said is that we're trying to make Perl 6 relatively safe for people who are just starting to use bits of it inappropriately in order to learn it. The term cargo cult also has some religious connotations, and to a small extent I mean those too. The religious wars fought in computer communities often arise among those with the least understanding of the deep issues. People much more easily fall into tribal behavior than into philosophical behavior. A deep understanding of bikesheds is that they're meant to hold bicycles, and it doesn't really matter what color they are. But when you first discover a bikeshed, you might mistake it for a tribal monument of some sort. It's obviously more important than the bicycle; after all, it's bigger. And we might attract more new husbands or wives from other tribes if we paint it attractively. So we spend a lot of time painting the outside while the bicycle is inside rusting. And the bicycle is what's important, because we should be reaching out to other tribes, not waiting for them to come to us. Well, hey, my metaphor is probably painted the wrong color. But maybe it's only by using analogies inappropriately that we can begin to understand how to use them appropriately. Larry
Re: txt vs OO [was: Re: Proposal to make class method non-inheritable]
--- Larry Wall wrote: On Tue, Oct 25, 2005 at 05:24:52PM +0200, Michele Dondi wrote: : But maybe that's just me. Whatever, I guess that the {casual,average} : programmer may be scared by its richness and complexity. But we're trying to design the OO features (indeed, all of Perl 6) such that you can usefully cargo cult those aspects that are of immediate interest without being forced to learn the whole thing. It's not the number one design goal, but it's right up there. Interesting. I just finished reading Stroustrup's thoughts on evolution and language design at: http://www.research.att.com/~bs/rules.pdf and noticed he is similarly concerned about making his language easier for the casual/novice programmer: If you are a physicist needing to do a few calculations a week, an expert in some business processes involving software, or a student learning to program, you want to learn only as many language facilities as you need to get your job done. He also argues in favour of general features over specialized ones: C++0x will not be a Windows language or a Web Language ... which reminded me of people characterizing Perl as a text processing language or a CGI language. Just as Perl 5 evolved Perl 4 from a scripting/text processing language to a general purpose one (with CPAN modules filling many specialized niches), Perl 6 seems to be evolving still further down that path, being applicable to more and varied domains than Perl 5 was. /-\ Send instant messages to your online friends http://au.messenger.yahoo.com
txt vs OO [was: Re: Proposal to make class method non-inheritable]
On Fri, 14 Oct 2005, Stevan Little wrote: I think Perl 6's OO system has the potential to be to OO programming what Perl 5, etc was to text processing. This, I believe, is in large part due to Sorry for replying so late. Thought it seems appropriate to post this in this time of Perl 6 fears and rants threads... Well, the point is that it is interesting to note that text processing is an _application area_, whereas OO programming is a programming language paradigm. Despite the intro above, this is not meant to be a rant or to express a fear. But it is intended to raise a meditation. After all, being known for text processing capabilities may be somewhat restictive and not faithful of Perl's (including Perl 5) full potentiality, but OO programming is somewhat immaterial either, the only relevance being the suitability for big projects management. Michele -- Ira Kane: If I was a giant nasty alien bird in a department store, where would I be? Harry Block: Lingerie. Ira Kane: Not you, the bird. Harry Block: Lingerie. - Evolution, Ivan Reitman
Re: txt vs OO [was: Re: Proposal to make class method non-inheritable]
On Oct 25, 2005, at 6:31 AM, Michele Dondi wrote: On Fri, 14 Oct 2005, Stevan Little wrote: I think Perl 6's OO system has the potential to be to OO programming what Perl 5, etc was to text processing. This, I believe, is in large part due to Sorry for replying so late. Thought it seems appropriate to post this in this time of Perl 6 fears and rants threads... Well, the point is that it is interesting to note that text processing is an _application area_, whereas OO programming is a programming language paradigm. Allow me to clarify. Perl 5 (and below) are known by outsiders (non-perl users) as being a really good language for processing text. Ask any friend you know who has had little or no exposure to Perl and they will probably tell you this. Of course they will also tell you that it is all line noise, etc, etc etc. This is the most common perception of Perl by those people who have (for the most part) never encountered it. I think that Perl 6 may become like that for OO. When people who have never used or encountered Perl 6 talk about it, they will say, I've never used it, but I hear it has lots of really cool OO features. Just as now they the same thing re: text-processing. Sure, this means nothing to people who are actually using it, but this is mostly about outside perception. These kinds of things are sometimes what will bring people to the language initially, so they are not to be taken lightly. Despite the intro above, this is not meant to be a rant or to express a fear. But it is intended to raise a meditation. After all, being known for text processing capabilities may be somewhat restictive and not faithful of Perl's (including Perl 5) full potentiality, Of course not, Perl is also used for CGI, but you can do that better with Java now (which is a real language cause it's compiled) ;) People who are not familiar with a language tend to rely heavily on the common knowledge about that language. And also tend to hold tightly to the myths and pseudo-facts surrounding their own languages. The combination of these two things tends to lead to silly statements like the one above. but OO programming is somewhat immaterial either, the only relevance being the suitability for big projects management. The idea that OO is only relevant for big projects is just as silly as saying that Perl is only good for text processing. Sure I would not use OO to write a small utility script, but the modules I access in the script would probably be written using OO. And those may be very small modules too, but their re-usability is greatly enhanced by various OO features (encapsulation, ability to compose-into or use within another class, etc etc etc). This kind of thing (IMHO) is why so many people are being drawn to Python and Ruby. Hopefully Perl 6 can draw them back. Stevan
Re: txt vs OO [was: Re: Proposal to make class method non-inheritable]
On Tue, 25 Oct 2005, Stevan Little wrote: Well, the point is that it is interesting to note that text processing is an _application area_, whereas OO programming is a programming language paradigm. Allow me to clarify. Perl 5 (and below) are known by outsiders (non-perl users) as being a really good language for processing text. Ask any friend you know who has had little or no exposure to Perl and they will probably tell you this. Of course they will also tell you that it is all line noise, etc, etc etc. This is the most common perception of Perl by those people who have (for the most part) never encountered it. ack! I think that Perl 6 may become like that for OO. When people who have never used or encountered Perl 6 talk about it, they will say, I've never used it, but I hear it has lots of really cool OO features. Just as now they the same thing re: text-processing. I see your point. And I admit I slightly misunderstood you. In any case people who don't know Perl and think it's great for text processing may be eventually become interested in it if they will have to do... ehm... text processing. But even if Perl will gain a name for it's cool OO features, will it become an actual interest for someone if he/she has to do... OO stuff?!? The basic idea being that one is attracted by a language or another if its features are well suited for some specific application he/she has in mind. Not for its features as an abstract thing. Also, Perl6 OO capabilities are already above the top of my head. But maybe that's just me. Whatever, I guess that the {casual,average} programmer may be scared by its richness and complexity. After all, being known for text processing capabilities may be somewhat restictive and not faithful of Perl's (including Perl 5) full potentiality, Of course not, Perl is also used for CGI, but you can do that better with Java now (which is a real language cause it's compiled) Of Perl is IMHO even _too_ known for CGI. but OO programming is somewhat immaterial either, the only relevance being the suitability for big projects management. The idea that OO is only relevant for big projects is just as silly as saying that Perl is only good for text processing. ack. I was oversimplifying. Sure I would not use OO to write a small utility script, but the modules I access in the script would probably be written using OO. And those may be very small modules too, but their re-usability is greatly enhanced by various OO features (encapsulation, ability to compose-into or use within another class, etc etc etc). This kind of thing (IMHO) is why so many people are being drawn to Python and Ruby. Hopefully Perl 6 can draw them back. Also, some methods will be very handy even for non-patently-OO blocks, e.g. the .pos() one which will replace the somewhat ad hoc pos() function or the .subst() one (or whatever it is called, I can't remember exactly) which hopefully will get rid of the why doesn't s/// work inside a map() question... Michele -- Have you ever stopped to consider that what is crashing your Perl is sitting between the keyboard and chair? - Sherm Pendley in clpmisc, Re: Perl IDE (edited)
Re: Proposal to make class method non-inheritable
Stevan Little [EMAIL PROTECTED] writes: Piers, On Oct 12, 2005, at 5:22 AM, Piers Cawley wrote: We definitely have two instances of A since, B.isa(::A). We also have a fragile implementation of count. :) Sorry, I purposefully made it a kludge as that is usually the way the example is shown in most tutorials about class methods. So, let me see if I have this straight here. You're arguing that, because people are often foolish, we should make it harder to be clever? And you're using a deliberately broken example as grist to your mill? Doesn't sound all that Perlish to me. -- Piers Cawley [EMAIL PROTECTED] http://www.bofh.org.uk/
Re: Proposal to make class method non-inheritable
Piers, On Oct 14, 2005, at 12:14 PM, Piers Cawley wrote: Stevan Little [EMAIL PROTECTED] writes: On Oct 12, 2005, at 5:22 AM, Piers Cawley wrote: We definitely have two instances of A since, B.isa(::A). We also have a fragile implementation of count. :) Sorry, I purposefully made it a kludge as that is usually the way the example is shown in most tutorials about class methods. So, let me see if I have this straight here. You're arguing that, because people are often foolish, we should make it harder to be clever? No, more that people have been lead down the wrong paths all too often based on limitations of language implementation. And that Perl 6 should be moving to correct this problem. Sure that might be un- Perlish in the sense that we are not leaving every way to do it open. But at some point I think you need to shake off the old accumulated crud and start fresh, even if that new way might go contrary to what some people have been conditioned to think. I also do not believe that I am making it harder, as much as I am making it different. Change is hard, but it *is* inevitable. I think Perl 6's OO system has the potential to be to OO programming what Perl 5, etc was to text processing. This, I believe, is in large part due to the fact that Perl 5 had such a slim object system, and Larry intended (based on what I have read in A12) to start fresh from the ground up. That, coupled with Perl's tendency to borrow the best from everywhere, and you have the potential for a really great OO system. And you're using a deliberately broken example as grist to your mill? The example is the canonical example for class methods/attributes, and yes it is broken. However, it's broken-ness only serves to illustrate, what I think is, the misunderstanding of the usefulness of class methods in general. One only needs to take a quick sampling of some of the more popular CPAN modules which sport an OO interface to see that all to often class methods are (ab)used to get what amounts to procedural modules with inheritence. Doesn't sound all that Perlish to me. Perl is many different things to different people, this is part of it's beauty as a language. Perl also has the unique ability to be able to re-invent itself on a regular basis (shell-scripts, CGI, bioinformatics, what next??). I personally think that the definition of what is Perlish and what is not Perlish is not only highly subjective, but ever changing. So I guess what I am saying here is thank you, as I will take that as a compliment ;) Stevan
Re: Proposal to make class method non-inheritable
HaloO, Stevan Little wrote: On Oct 11, 2005, at 8:47 PM, Larry Wall wrote: You seem to be arguing that a class has no state, but my view is that, in the abstract, a class encompasses the state of *all* its objects. It just hasn't picked one particular object to be at the moment. I love this notion because that makes the type that the class represents the potential state it would reach when *all* distinguishable instances were realized---which needs a lot of memory for many classes. But the bit class/type e.g. has exactly two distinguishable states! Well and more then these two will never be needed if the algebraic, boolean type is build with !, and ||. Negation just exchanges the half of the type that is in use, the former one is returned into potentiality, that is, it leaves the programs actuality. The binary connectives leave false and true in the program respectively as soon as both booleans have entered the program! That is (1 0 == 0) and (0 || 1 == 1) dual each other, just as the unrealized potential state of a class duals the actually realized state of the instances of the class in any particular moment. Here's a nice magic square picture of the idea: -Class .. ::Classmeta space ** | /| | / |The diagonal line splits the potential(-) from | / |the actual(+) instances. Undef and the ::Class |/ |are the singularities of the class. The state | /|of the class moves up the diagonal through instance | / |creation, while the GC moves it towards Undef. | / | ** Undef .. +Classvalue space No, not that class has no state, but that with the currently specced classes we have inherited behaviors (class methods) but they do not inherit the accompanying state (class attributes) as well. I see this as potentially very problematic. What do you mean with not inheriting class state? I would think that beeing an instance of a class, an object should have access to the shared class data, but it is not part of the per object state. Or do you mean that class data is not accessable to the instances at all? I would hope that this is 'hidden' in the scopes where the class's parts are defined but of course accessable lexically from class support code there. --
Re: Proposal to make class method non-inheritable
On Oct 13, 2005, at 4:45 PM, TSa wrote: No, not that class has no state, but that with the currently specced classes we have inherited behaviors (class methods) but they do not inherit the accompanying state (class attributes) as well. I see this as potentially very problematic. What do you mean with not inheriting class state? I would think that beeing an instance of a class, an object should have access to the shared class data, but it is not part of the per object state. Or do you mean that class data is not accessable to the instances at all? I would hope that this is 'hidden' in the scopes where the class's parts are defined but of course accessable lexically from class support code there. I mean that classes do not inherit class state along subclass lines. Take this code for instance: class Foo { has $.bar; } every instance of Foo I create has it's own copy of $.bar. Now I subclass it: class Bar is Foo {} every instance of Bar has it's own copy of $.bar as well. Now look at this from the class attribute perspective. class Foo { our $.baz; # class data here,.. not instance data } Foo has a single copy of $.baz in it's internal namespace. Now subclass it: class Bar is Foo {} Bar does not have it own copy of $.baz, in fact Bar's ability to access Foo::{$.baz} (or however it would be spelt) is not more special than any other class in the system. The point I am trying to make is that class data is unique to the class itself, and subclasses of said class do not have any special priviledges or access to that data (leaving auto-generated accessors out of the picture for now). This is different though from how class methods behave, which seems to me to be problematic. Stevan
RE: Proposal to make class method non-inheritable
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.) : 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. 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 ... } Rather than having to make plugin creation cheap so that I can do this: class Host { public Type PlugInClass { get { ... } set { ... } } } class PlugIn { public bool Host { get; } public abstract bool SupportsFeatureB { get; } public abstract bool SupportsFeatureA { get; } } ... later ... PlugIn plugIn = (PlugIn) Activator.CreateInstance(host.PlugInClass); if (plugIn.SupportsFeatureA) { ... expose UI element ... } Another alternative is to introduce a PlugInFactory. Which might be better in complex scenarios. But it's often overkill. And it's certainly more typing. In C#, I might also use attributes to decorate the plugin Type. But those, too, are more typing than class methods (and also weakly typed). 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 -- Gordon Henriksen [EMAIL PROTECTED]
Re: Proposal to make class method non-inheritable
Stevan Little [EMAIL PROTECTED] writes: Hello all. I would like to propose that class methods do not get inherited along normal class lines. I think that inheriting class methods will, in many cases, not DWIM. This is largely because your are inheriting behavior, and not state (since class attributes are not inheritable). Let me explain in more detail. Let's start by making a very basic definition of an *object*, ignoring any implementation details or specifics. object == state + behavior This statement assumes that *objects* at their core are a unique state coupled with a collection of behaviors to act upon that particular state. Of course we are ignoring all the other class/meta/ inheritence junk for now. To take away the behavior, and only be left with state would degrade our object to the level of C struct or Pascal-style record-type. To take away the state, and only be left with behavior, would basically leave a module/package or some pseudo-random collection of functions. So at this point, I think it is safe to say that an *object* should have both state and behavior. Now, back down from the theoretical cloud to reality. I would like to show some canonical class-method examples (and in some cases, show how they are broken), then show how they might be better accomplished in Perl 6 without the need for class methods to be inherited. == Instance Counting Class The most common example given for class methods is an instance counter. Here is how one might (naively) look in Perl 6: class A { our $.count; method count (Class $c:) { $.count; } submethod BUILD { $.count++; } } Each time an instance of A is created the counter is incremented. So that ... A.count; # 0 A.new; A.count; # 1 Now this makes sense, until we subclass A. class B is A {} A.count; # still 1 B.new; # calls A::BUILD A.count; # 2 B.count; # 2 Clearly, we only have one instance of A, and one instance of B, so those numbers are wrong. It could be argued that since B is a subtype of A, we do have two A's, but the argument does not work in reverse. But either way, I would argue that the results shown above are misleading, and probably not what the programmer intended. We definitely have two instances of A since, B.isa(::A). We also have a fragile implementation of count. class A { our %.count_of method count (Class $c:) { %.count_of{$c} } method BUILD { $class = ($?SELF.class) @countable_ancestors = $class.ancestors.uniq.grep :{.isa(::A)} for $class, [EMAIL PROTECTED] - $c { %.count_of{$c}++ } } Where we're assuming I've got the syntax of 'for' right, and that 'ancestors' is a class method that returns all of a class's ancestors. This might not work too well in the face of a dynamic inheritance tree, but it should be possible to work around. Something like this might work: Class A { our %.instance_count_of method count (Class $c: ?$with_descendents = undef) { my @interesting_classes = $c; if $with_descendents { push @interesting_classes, *($c.all_subclasses); } [+] %.instance_count_of(@interesting_classes) } method BUILD { %.instance_count_of($?SELF.class) } } Where we're assuming that a class can find all its subclasses -- Piers Cawley [EMAIL PROTECTED] http://www.bofh.org.uk/
Re: Proposal to make class method non-inheritable
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 ^ ..:. : : eFooeBar ^ ^ | | 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
Re: Proposal to make class method non-inheritable
Piers, On Oct 12, 2005, at 5:22 AM, Piers Cawley wrote: We definitely have two instances of A since, B.isa(::A). We also have a fragile implementation of count. :) Sorry, I purposefully made it a kludge as that is usually the way the example is shown in most tutorials about class methods. class A { our %.count_of method count (Class $c:) { %.count_of{$c} } method BUILD { $class = ($?SELF.class) @countable_ancestors = $class.ancestors.uniq.grep :{.isa(::A)} You can use the MRO here, which is an already linearized list of the inheritance tree (in C3 order) with all duplicates removed. for $class, [EMAIL PROTECTED] - $c { %.count_of{$c}++ } } Where we're assuming I've got the syntax of 'for' right, and that 'ancestors' is a class method that returns all of a class's ancestors. This might not work too well in the face of a dynamic inheritance tree, but it should be possible to work around. Something like this might work: Class A { our %.instance_count_of method count (Class $c: ?$with_descendents = undef) { my @interesting_classes = $c; if $with_descendents { push @interesting_classes, *($c.all_subclasses); } [+] %.instance_count_of(@interesting_classes) } method BUILD { %.instance_count_of($?SELF.class) } } Where we're assuming that a class can find all its subclasses -- Piers Cawley [EMAIL PROTECTED] http://www.bofh.org.uk/
Re: Proposal to make class method non-inheritable
Gordon, It just occurred to me that the system shown below could be re- written to do away with class methods entirely. class Host { my $.plugInClass; } role PlugIn { method initWithHost (Host $h:) { ... } } role FeatureA {} role FeatureB {} role FeatureC {} class AB { does PlugIn; does FeatureA; does FeatureB; } class ABC { does AB; does FeatureC; } Now later on, instead of asking the PlugIn if it supportsFeatureB, you can just see if it does the FeatureB role, like this: if ($host.plugInClass.does('FeatureB')) { # ... do something with FeatureB } This will work with the ABC plugin as well since AB is being treated as a role, ABC will actually consume all it's subroles, which means that ABC will DWIM too. In fact, we get even more from this system since we can check if one plug-in is capable of doing another, because this just works if ($host.plugInClass.does('AB')) { # ... } And since an example is better when it is backed up by working code, I coded this up using the current meta-model prototype. You can see it here: http://svn.openfoundry.org/pugs/perl5/Perl6-MetaModel/t/ 38_PlugIn_example.t Stevan On Oct 12, 2005, at 9:41 AM, Stevan Little wrote: 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; } role SupportsFeatureC { method supportsFeatureC (Class $c:) { bool::true } } class ABC { does AB; does SupportsFeatureC; }
Re: Proposal to make class method non-inheritable
Larry, On Oct 11, 2005, at 8:47 PM, Larry Wall wrote: On Tue, Oct 11, 2005 at 06:10:41PM -0400, Stevan Little wrote: : Hello all. : : I would like to propose that class methods do not get inherited along : normal class lines. I think most class methods should be written as submethods instead. In which case they would not be inherited then. I (obviously) agree with you on that :) You seem to be arguing that a class has no state, but my view is that, in the abstract, a class encompasses the state of *all* its objects. It just hasn't picked one particular object to be at the moment. No, not that class has no state, but that with the currently specced classes we have inherited behaviors (class methods) but they do not inherit the accompanying state (class attributes) as well. I see this as potentially very problematic. : == Instance Counting Class : : The most common example given for class methods is an instance : counter. Here is how one might (naively) look in Perl 6: : : class A { : our $.count; : method count (Class $c:) { $.count; } : submethod BUILD { : $.count++; : } : } That's obviously broken--the count accessor should be a submethod to be consistent, unless the explicit intent is that any subclass of A return the count of A's. Which, not surprisingly, is exactly what you get below. It should probably have been declared: our $.A_count; in that case. And in which case you don't need the explicit accessor, since one would have been provided because of the dot. If you don't want the autoaccessor, don't use the dot. Yes, this example was purposefully broken, but also copied from several tutorial on how to use class methods. I suppose an argument could be made that autoaccessors for class vars should be submethods by default. Or maybe my $.count makes a submethod, while our $.A_count makes a method. I think that is probably not a bad idea. If we not going to inherit the class state, then we should not inherit the class method either. : Clearly, we only have one instance of A, and one instance of B, so : those numbers are wrong. It could be argued that since B is a subtype : of A, we do have two A's, but the argument does not work in reverse. Sure it does. It doesn't matter whether B is a subtype of A or not, you've given it an interface to code that counts A's. True, that was probably a bad example (culled from other bad examples). : But either way, I would argue that the results shown above are : misleading, and probably not what the programmer intended. That's certainly possible, but it wouldn't be the first time people have been surprised that the computer did exactly what they asked it to... :-) Also very true. : Sure, you could do that, however, it complicates the meta-model : unnecessarily. It is much easier to accomplish this using a subclass : of Class. : : class CountingClass is Class { : has $.counter; : method count (CountingClass $c:) { $.counter; } : method new (CountingClass $c: %params) { : $.counter++; : next; : } : } : : class A meta CountingClass {} : class B meta CountingClass {} : : Now A and B both have their own counters neither of which interfere : with one another. Only by forcing people to repeat themselves as a matter of policy. And policy could just as easily have added our $.count to B. The real trick would be to set up A such that you don't have to do anything special in B. I suppose we could say that, by default, if A isa B, then A gets also gets whatever metaclass B has, not the standard metaclass supplied by class. I considered this as well, it seems to make some sense that the metaclass of A is also the metaclass of B if A isa B. This would actually simplify a particular edge case with the eigenclasses that was problematic. This diagram assumes we have inheritable class methods, and they are implemented using the eigenclasses. Class ^ : eFoo...eBar ^ ^ | | Foo...Bar The 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. When you introduce a custom metaclass like so: Class ^ : CustomMeta ^ : eFoo...eBar ^ ^ | | Foo...Bar A problem occurs with method dispatch for Bar. First it will go to eBar, then to eFoo, then to CustomMeta, then to Class, etc, etc. Since Bar was not explicitly created with CustomMeta, this is not correct. However, if the metaclasses act as you describe, this is then does exactly what it is supposed to. I think it is a sane approach personally. But these are metaclasses, not classes. You keep writing the type of the invocant of class methods as Class, but I don't believe that anymore. Sorry, just following the A12 examples :) The type of the invocant of a class
Re: Proposal to make class method non-inheritable
Gordon, On Oct 12, 2005, at 10:48 AM, Gordon Henriksen wrote: Actually, I wondered why you didn't suggest this earlier. :) I figured you were a step ahead of me: What if I want more than a boolean out of my class method? Then you put the class methods back in :) But then your Objective-C interface would need to change too. Although, the more complexity you introduce, the closer you get to the point when a Factory pattern is just as viable an approach as class methods. Stevan On Oct 12, 2005, at 10:27, Stevan Little wrote: Gordon, It just occurred to me that the system shown below could be re- written to do away with class methods entirely. class Host { my $.plugInClass; } role PlugIn { method initWithHost (Host $h:) { ... } } role FeatureA {} role FeatureB {} role FeatureC {} class AB { does PlugIn; does FeatureA; does FeatureB; } class ABC { does AB; does FeatureC; } Now later on, instead of asking the PlugIn if it supportsFeatureB, you can just see if it does the FeatureB role, like this: if ($host.plugInClass.does('FeatureB')) { # ... do something with FeatureB } This will work with the ABC plugin as well since AB is being treated as a role, ABC will actually consume all it's subroles, which means that ABC will DWIM too. In fact, we get even more from this system since we can check if one plug-in is capable of doing another, because this just works if ($host.plugInClass.does('AB')) { # ... } And since an example is better when it is backed up by working code, I coded this up using the current meta-model prototype. You can see it here: http://svn.openfoundry.org/pugs/perl5/Perl6- MetaModel/t/38_PlugIn_example.t Stevan On Oct 12, 2005, at 9:41 AM, Stevan Little wrote: 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; } role SupportsFeatureC { method supportsFeatureC (Class $c:) { bool::true } } class ABC { does AB; does SupportsFeatureC; } — Gordon Henriksen [EMAIL PROTECTED]
Re: Proposal to make class method non-inheritable
Gordon, On Oct 12, 2005, at 11:04 AM, Gordon Henriksen wrote: On Oct 12, 2005, at 09:41, Stevan Little wrote: 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. For varying definitions of initialized. I never much cared for the bare poke stuff straight into my instance variables constructor along the lines of: sub new { my($class, %ARGS); return bless \%ARGS, $class; } Yes, that is a horrible idiom which I hope will die in Perl 6. That more or less robs the constructor of the behavior part of class = state + behavior. I need an opportunity to establish my invariants. Well that is where BUILD comes in, you can do all sorts of mangling of parameters in BUILD so that it does what you want it to, there is no real need to put this behavior into new. Of course, when there is no such behavior, it saves a lot of repetitive typing in the class. C# 3 is finally growing a syntax that resolves this by having the language do the repetitive typing at the call site... X x = new X{ Y = 1, Z = 2 }; means X x = new X(); x.Y = 1; x.Z = 2; And X doesn't need anything but the default constructor. Yes, this is exactly what the new - CREATE - BUILDALL - BUILD chain is doing too. 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). This is more or less how class methods have to work. I would go a bit further, though. Too implement this: Foo : Object Foo : Bar The runtime should use an inheritance tree as such: Object Class : Object Foo : Object Bar : Foo _Object : Class _Class : _Object _Foo : _Class _Bar : _Foo Note that every declared class, including Object and Class themselves, have an anonymous Class subclass that precisely parallels the declared inheritance chain. (Chicken and egg problem? Probably. Object and Class are Special.) This is pretty much the same thing that I am describing, except that I don't think that Class needs an anon-class/eigenclass. All object models need a cycle at the top, this keeps the turtles-all-the-way- down problem away. The usual place is to put the cycle in Class (Class is an instance of Class). If you add that anon-class, you break the cycle. With this implementation, there are three places to put state: In MyObject (instance variable), in _MyObject (class instance variable), or outside of any instance (class variable). The class instance variable is the least useful of the three. Well, I would argue that class instance variables are very useful, since that is where methods and attribute meta-objects are stored. But I think users should not have direct access to class instance variables. But yes, other than that you are correct. Note: I don't see much value in invoking class methods through instances, since a Foo IS_NOT_A Class. If one wants to save the user typing .class when invoking class methods through an instance, Yes, that is bad, another nasty p5 idiom I hope will go away. I would tend toward resolving it as such: class Foo { class_method int Bar(int i) { return i * i; } } -- BECOMES -- # Common interface for Foo's class methods. interface _IFoo { method int Bar(int i); } # The anonymous Class. class _Foo extends Class implements _IFoo { int Bar(int i) { return i * i; } } # The visible class. class Foo implements _IFoo { # Forwards the call to the Class. void Bar(...) { return this.Class.Bar(...); } } I think we can leave the interface part out, but yes, this is basically how the eigenclasses work :) I'll leave the probably obvious role-based interpretation of this to those versed in such. :) s/interface/role/ and you have the role based version ;) Stevan
Re: Proposal to make class method non-inheritable
On Oct 12, 2005, at 09:41, Stevan Little wrote: 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. For varying definitions of initialized. I never much cared for the bare poke stuff straight into my instance variables constructor along the lines of: sub new { my($class, %ARGS); return bless \%ARGS, $class; } That more or less robs the constructor of the behavior part of class = state + behavior. I need an opportunity to establish my invariants. Of course, when there is no such behavior, it saves a lot of repetitive typing in the class. C# 3 is finally growing a syntax that resolves this by having the language do the repetitive typing at the call site... X x = new X{ Y = 1, Z = 2 }; means X x = new X(); x.Y = 1; x.Z = 2; And X doesn't need anything but the default constructor. 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). This is more or less how class methods have to work. I would go a bit further, though. Too implement this: Foo : Object Foo : Bar The runtime should use an inheritance tree as such: Object Class : Object Foo : Object Bar : Foo _Object : Class _Class : _Object _Foo : _Class _Bar : _Foo Note that every declared class, including Object and Class themselves, have an anonymous Class subclass that precisely parallels the declared inheritance chain. (Chicken and egg problem? Probably. Object and Class are Special.) With this implementation, there are three places to put state: In MyObject (instance variable), in _MyObject (class instance variable), or outside of any instance (class variable). The class instance variable is the least useful of the three. Note: I don't see much value in invoking class methods through instances, since a Foo IS_NOT_A Class. If one wants to save the user typing .class when invoking class methods through an instance, I would tend toward resolving it as such: class Foo { class_method int Bar(int i) { return i * i; } } -- BECOMES -- # Common interface for Foo's class methods. interface _IFoo { method int Bar(int i); } # The anonymous Class. class _Foo extends Class implements _IFoo { int Bar(int i) { return i * i; } } # The visible class. class Foo implements _IFoo { # Forwards the call to the Class. void Bar(...) { return this.Class.Bar(...); } } I'll leave the probably obvious role-based interpretation of this to those versed in such. :) And I'm going to shut my yap, now, having butted into the middle of a discussion of a hopelessly complex runtime that I haven't been following for a 18 months. :) — Gordon Henriksen [EMAIL PROTECTED]
Re: Proposal to make class method non-inheritable
On Wed, 2005-10-12 at 12:00 -0400, Stevan Little wrote: Usefulness aside, why do Roles and Classes need to be seperate beasts? In the current meta-model prototype, the role system is laid atop the class system so that the following is true: Class is an instance of Class Role is an instance of Class Class does Role This then means that Role also .does Role since Role is an instance of Class (which does Role). It gets very cyclical, but it essentially means that all classes can be treated as roles. This allows for all sorts of interesting things to happen actually. I've always thought that classes were more specific than roles in that you can't apply a class to another class. (Maybe that's the wrong direction of specificity. Though I am positive that .does() is more general than .isa(), I always have to stop and think about which direction co- and contra-variance goes.) Certainly I think declaring a class should imply a role of the same name, even if you can't actually apply that role and mix in state and behavior. I have to admit though, that this comes directly from Scala (so maybe we are not alone here out on the edge :) I've also heard that Sather does something similar but don't know any details. -- c
Re: Proposal to make class method non-inheritable
Actually, I wondered why you didn't suggest this earlier. :) I figured you were a step ahead of me: What if I want more than a boolean out of my class method? On Oct 12, 2005, at 10:27, Stevan Little wrote: Gordon, It just occurred to me that the system shown below could be re- written to do away with class methods entirely. class Host { my $.plugInClass; } role PlugIn { method initWithHost (Host $h:) { ... } } role FeatureA {} role FeatureB {} role FeatureC {} class AB { does PlugIn; does FeatureA; does FeatureB; } class ABC { does AB; does FeatureC; } Now later on, instead of asking the PlugIn if it supportsFeatureB, you can just see if it does the FeatureB role, like this: if ($host.plugInClass.does('FeatureB')) { # ... do something with FeatureB } This will work with the ABC plugin as well since AB is being treated as a role, ABC will actually consume all it's subroles, which means that ABC will DWIM too. In fact, we get even more from this system since we can check if one plug-in is capable of doing another, because this just works if ($host.plugInClass.does('AB')) { # ... } And since an example is better when it is backed up by working code, I coded this up using the current meta-model prototype. You can see it here: http://svn.openfoundry.org/pugs/perl5/Perl6- MetaModel/t/38_PlugIn_example.t Stevan On Oct 12, 2005, at 9:41 AM, Stevan Little wrote: 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; } role SupportsFeatureC { method supportsFeatureC (Class $c:) { bool::true } } class ABC { does AB; does SupportsFeatureC; } — Gordon Henriksen [EMAIL PROTECTED]
Re: Proposal to make class method non-inheritable
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. 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. 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. In my opinion, class method inheritance is an important part of class-based OO--almost as important as object method inheritance. Removing features simply because their implementation is inconvenient is not The Perl Way. If it were, Perl 6 would be Java With Sigils. -- Brent 'Dax' Royal-Gordon [EMAIL PROTECTED] Perl and Parrot hacker
Re: Proposal to make class method non-inheritable
All - I'm partly to blame for this thread because I put the idea into Steve's head that class methods being inheritable may be dogma and not a useful thing. Mea culpa. That said, I want to put forward a possible reason why you would want class methods to be inheritable - to provide pure functions. If we go back to every function/method/subroutine being behavior, it's pretty evident that you need some state for that behavior to operate upon. In a procedural function, the state is provided. In an object, that state is both provided and inherent in the instance the method is operating upon. In functional languages, functions maintain their own state through closures, continuations, and the like. I might want to provide in a base class a set of useful sorting routines or other such items. These would be true procedural functions and there would be a warning thrown if it accessed class state. Rob
Re: Proposal to make class method non-inheritable
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
Re: Proposal to make class method non-inheritable
Anyway, I have said my peace, what do you all think? I think there are serious problems with this proposal. For a start, it would be very difficult to create *any* objects at all if the Cnew() class method wasn't inheritable. Damian
Re: Proposal to make class method non-inheritable
Stevan Little wrote: I would like to propose that class methods do not get inherited along normal class lines. One of the things that has annoyed me with Java is that it's class methods don't inherit (dispatch polymorphically). This means that you can't apply the template method pattern to static (class) methods. I hope Perl 6 doesn't copy this feature.
Re: Proposal to make class method non-inheritable
David, On Oct 11, 2005, at 7:49 PM, Dave Whipp wrote: Stevan Little wrote: I would like to propose that class methods do not get inherited along normal class lines. One of the things that has annoyed me with Java is that it's class methods don't inherit (dispatch polymorphically). This means that you can't apply the template method pattern to static (class) methods. I hope Perl 6 doesn't copy this feature. If you would please give a real-world-useful example of this usage of class-methods, I am sure I could show you, what I believe, is a better approach that does not use class methods. As for Java's static methods, they are very different from class methods in Perl 6. Java's static methods are little more than functions which have access to other static members. This feature is available in Perl 5 now in the form of subs in a package namespace and package scoped variables. The fact that Java uses the method call syntax to access static methods is (IMHO) just an attempt to have consistency in calling conventions. I say this because (as you pointed out) they share very little with other methods. Stevan
Re: Proposal to make class method non-inheritable
Stevan Little wrote: David, ... If you would please give a real-world-useful example of this usage of class-methods, I am sure I could show you, what I believe, is a better approach that does not use class methods. ... The example I've wanted to code in Java is along the lines of: public class Base { public static main(String[] args) { init(); do_it(args); cleanup() } } and then define a bunch of derived classes as my main class. public class Derived extends Base { static init() { print(doing init); } static do_it(String[] args) { print(doing body); } static cleanup() { print(doing cleanup); } } % javac Derived % java Derived In other words, I wanted to not have a main function on the class that I run as the application. This example, of course, doesn't apply to Perl -- but I think that the basic pattern is still useful
Re: Proposal to make class method non-inheritable
On Tue, Oct 11, 2005 at 06:10:41PM -0400, Stevan Little wrote: : Hello all. : : I would like to propose that class methods do not get inherited along : normal class lines. I think most class methods should be written as submethods instead. : I think that inheriting class methods will, in many cases, not DWIM. : This is largely because your are inheriting behavior, and not state : (since class attributes are not inheritable). Let me explain in more : detail. : : Let's start by making a very basic definition of an *object*, : ignoring any implementation details or specifics. : : object == state + behavior : : This statement assumes that *objects* at their core are a unique : state coupled with a collection of behaviors to act upon that : particular state. Of course we are ignoring all the other class/meta/ : inheritence junk for now. : : To take away the behavior, and only be left with state would degrade : our object to the level of C struct or Pascal-style record-type. To : take away the state, and only be left with behavior, would basically : leave a module/package or some pseudo-random collection of functions. : : So at this point, I think it is safe to say that an *object* should : have both state and behavior. You seem to be arguing that a class has no state, but my view is that, in the abstract, a class encompasses the state of *all* its objects. It just hasn't picked one particular object to be at the moment. : Now, back down from the theoretical cloud to reality. I would like to : show some canonical class-method examples (and in some cases, show : how they are broken), then show how they might be better accomplished : in Perl 6 without the need for class methods to be inherited. : : == Instance Counting Class : : The most common example given for class methods is an instance : counter. Here is how one might (naively) look in Perl 6: : : class A { : our $.count; : method count (Class $c:) { $.count; } : submethod BUILD { : $.count++; : } : } That's obviously broken--the count accessor should be a submethod to be consistent, unless the explicit intent is that any subclass of A return the count of A's. Which, not surprisingly, is exactly what you get below. It should probably have been declared: our $.A_count; in that case. And in which case you don't need the explicit accessor, since one would have been provided because of the dot. If you don't want the autoaccessor, don't use the dot. I suppose an argument could be made that autoaccessors for class vars should be submethods by default. Or maybe my $.count makes a submethod, while our $.A_count makes a method. : Each time an instance of A is created the counter is incremented. So : that ... : : A.count; # 0 : A.new; : A.count; # 1 : : Now this makes sense, until we subclass A. : : class B is A {} : : A.count; # still 1 : B.new; # calls A::BUILD : : A.count; # 2 : B.count; # 2 : : Clearly, we only have one instance of A, and one instance of B, so : those numbers are wrong. It could be argued that since B is a subtype : of A, we do have two A's, but the argument does not work in reverse. Sure it does. It doesn't matter whether B is a subtype of A or not, you've given it an interface to code that counts A's. : But either way, I would argue that the results shown above are : misleading, and probably not what the programmer intended. That's certainly possible, but it wouldn't be the first time people have been surprised that the computer did exactly what they asked it to... :-) : What is happening here is that we are inheriting behavior, but not : inheriting state. Which goes against the core definition of *objects*. I'd say that state is precisely what we're inheriting here. It's just the state of something that is not the object. But that was already true of class A as well. : I can solve this, just make class attributes inheritable?, you say. Don't see how that would help, unless you also force every class to redeclare the attributes. (By the way, attributes of the form $.count are already inheritable, being shorthand for $?SELF.count.) : Sure, you could do that, however, it complicates the meta-model : unnecessarily. It is much easier to accomplish this using a subclass : of Class. : : class CountingClass is Class { : has $.counter; : method count (CountingClass $c:) { $.counter; } : method new (CountingClass $c: %params) { : $.counter++; : next; : } : } : : class A meta CountingClass {} : class B meta CountingClass {} : : Now A and B both have their own counters neither of which interfere : with one another. Only by forcing people to repeat themselves as a matter of policy. And policy could just as easily have added our $.count to B. The real trick would be to set up A such that you don't have to do anything special in B. I suppose we could say that, by default, if A isa B, then A gets also
Re: Proposal to make class method non-inheritable
David, On Oct 11, 2005, at 8:42 PM, Dave Whipp wrote: Stevan Little wrote: David, ... If you would please give a real-world-useful example of this usage of class-methods, I am sure I could show you, what I believe, is a better approach that does not use class methods. ... The example I've wanted to code in Java is along the lines of: public class Base { public static main(String[] args) { init(); do_it(args); cleanup() } } and then define a bunch of derived classes as my main class. public class Derived extends Base { static init() { print(doing init); } static do_it(String[] args) { print(doing body); } static cleanup() { print(doing cleanup); } } % javac Derived % java Derived In other words, I wanted to not have a main function on the class that I run as the application. This example, of course, doesn't apply to Perl -- but I think that the basic pattern is still useful I think this example is constrained by the way Java handles the main static method. This same pattern could be done using instance methods, and a little bit of reflection in Java. public interface Base { public void init; public void do_it(String[] args); public void cleanup; } public class BaseRunner { public static main (String[] args) { ClassLoader cl = new java.lang.ClassLoader(); Class c = cl.findClass(args[0]); Base b = c.newInstance(); b.init(); b.do_it(args); b.cleanup(); } } public class Derived implements Base { public void init() { print(doing init); } public void do_it(String[] args) { print(doing body); } public void cleanup() { print(doing cleanup); } } NOTE: this code is untested :) This version actually allows you to vary the subclasses though the command line arguments, which provides even greater flexibility and does not require you to recompile BaseRunner or Base. Doing something similar in Perl 6 is even easier than the Java version. Stevan