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 method in A is A, not Class.  It
just happens to be a generic A, not a specific A, and tests as undefined.

But the "type" of A is a Class correct?

If so, then this becomes a special case in the parameter parsing which can take an instance rather than just the "type-of" an instance.

If we follow this more, then do we end up with things like Prolog/ML/ Erlang style pattern matching?

  multi length([])      { 0 }
  multi length(List @l) { 1 + length(@l.tail) }

Where params can be either (type => name) pairs, or some kind of prototypical instance which we try to match against.

So then the class methods would actually look like this maybe:

 method my_class_method (::A) { ... }


: 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.

You can already do this sort of thing using the trait syntax.
That's why I go on about "traitorous traits" in A12: "is foo" can
do *anything* to the container on its left, including installing
new parent classes and metaclasses.  (But if I did want to define an
important new default-overriding metaclass, I'd probably replace the
"class" keyword with a word like "role", or "union", or "theory",
or whatever I thought was appropriate.  Not sure counters rise to
that level though.)

I thing that would maybe be overkill for specificing custom metaclasses. Wouldn't it suffer from the same "repeating yourself" policy issues you described above?

: 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 }".

Well, hey, we're out ahead of the state of the art here, so we can
do whatever we like with roles.

At this point, I assume that anything and everything inside a role is consumed by the class, so that the role can be then be disposable (unless of course another class wants to use it).


: == 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.

The purpose of custom constructors in Perl 6 is to allow the use of
positional args.  I can't think of any other use for them, offhand...

That's all I got too. And I am not sure you would always want to that be inherited either.

: 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).

I think that, at least as a name, the default new() *should* be in
Object, though of course most of its work might done by Object.meta
instead.

So &new in Object a class method? Why? It works just fine as an instance method of Class.

I don't believe in Class as a class anymore, unless that's
what .meta reaches these days.

What would Class be if it is not itself an instance of Class?

If you want to change the behavior
of a class, you're really talking about changing the behavior of
the metaclass instance (which typically stores various bits of its
data in a package).  The A class is just a proxy for all that, so
that you can reason about objects of type A without actually having
one.

Having an instance of Class which describes A allows you to reason about A very easily without needing an instance of A. I think maybe the ways in which you reason about it might be different, but there is no reason you cannot do so. A Class instance should respond to .isa and .does exactly the same way an instance of said class would. The rest can be done with .meta introspection on superclasses, methods and attributes. Classes are just more complex beasts than instances, I think a reflection API/Meta Object Protocol will give you all that you need and more.

I'm beginning to suspect Class is just a mixin to that breaks the
instance association, such that if you did

    $fido = new Dog;
    $fido does Class;

you'd end up with $fido undefined (but still of type Dog).

I am not sure that code makes sense. How would that be useful?

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 have to admit though, that this comes directly from Scala (so maybe we are not alone here out on the edge :)

So basically, you declare your constructor

    method new (Class $c: ...)

if you want to restrict $c to something that does the Class *role*, which
guarantees there's no associated instance.

I think this could be accomplished by just saying "parameter checking checks .does() and not .isa()" (which would make chromatic happy). But I am not sure there is a need to make Class a role.

But if you're more into prototyping, you'd say

    method modified (Dog $d: ...)

if you want to restrict $d to something that does Dog, and you don't
care whether it happens to have an instance associated because the
class already defines decent defaults for any attributes you don't
override.

I think this would work as well if parameters checked .does() since the Dog class surely .does(Dog).

I want Perl 6 to encompass both class-based and prototype-based
approaches, and this seems like the right way to do it.

I think given enough meta-accessibility, a model whose core is class- based can be easily used as prototype-based.

I think there's no such thing as a Class instance.  That simplifies
things even further.

Do you mean no instance of Class called "Class"? or no instances of Class at all? If so, how would we represent classes?

Stevan

Reply via email to