develooper Front page | perl.perl6.language | Postings from October 2005

Proposal to make class method non-inheritable

Thread Next
From:
Stevan Little
Date:
October 11, 2005 15:12
Subject:
Proposal to make class method non-inheritable
Message ID:
0F8720A4-5418-4A09-BC47-CB3A6BDD599C@iinteractive.com
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.

What is happening here is that we are inheriting behavior, but not  
inheriting state. Which goes against the core definition of *objects*.

"I can solve this, just make class attributes inheritable?", you say.

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. Of course the "meta" syntax there is speculative,  
but surely you can accomplish that behavior somehow. This approach  
actually uses no class methods, only instance methods.

However, as always, there is more than one way to do it, you can  
accomplish the same thing using Roles. Here is how that might look:

role Countable {
     our $.count;
     method count (Class $c:) { $.count; }
     submethod BUILD {
         $.count++;
     }
}

CountingA = A but Countable;
CountingB = B but Countable;

CountingA.count; # 0
CountingA.new;
CountingA.count; # 1

CountingB.count; # 0
CountingB.new;
CountingB.count; # 1

NOTE: I am assuming that the Countable role will mix-in the class  
method &count as well as the class attribute $.count. And that "A but  
Countable" is really sugar for something like "class { does  
Countable; is A }".

== Custom Constructors

Another common example of class method usage is custom constructors.  
This example is moot given the BUILDALL/BUILD system. All class  
specific initialization can easily be done using custom BUILD  
submethods.

This example too is skewed towards a language's particular object  
model as well. In Java/C# the constructor is a special/magical  
"thing" which is called by the "new" keyword. In Smalltalk, "new" is  
actually an instance method of the class Class, and it calls the  
specific object's "new" instance method to initialize (somewhat like  
the CREATE->BUILDALL/BUILD in Perl 6). In the Perl6-MetaModel  
prototype, &new is implemented as an instance method of Class, and  
not a class method of Object (as is sometimes assumed).

== Java-style static methods

Java's static methods are only thought of as being like class methods  
because they have access to other static class members, and they are  
only callable "though" the class. The Perl 6 equivalent of this  
concept is nothing more than a package sub, and a package variable.  
Since Class isa Package, this type of behavior is easily accomplished.

== Conclusion

Now, I am not proposing we abolish class methods entirely, only that  
we simplify them. If we do not require that class methods be  
inherited, then they can be implemented as ruby-style "singleton- 
methods" on the Class instance. This keeps the internals of the meta- 
model clean and orderly (always a good thing IMHO :).

Anyway, I have said my peace, what do you all think?

Thanks,

Stevan





Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About