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




Reply via email to