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