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