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




Reply via email to