On Jul 24, 2005, at 2:40 AM, Sam Vilain wrote:
Stevan Little wrote:
Yes, we have. One thing to consider is that it is much easier to get
the "Role order doesn't matter" thing when they are composed. Once
you start keeping the roles around, you run into the possiblity for
such things as "next METHOD" being executed in Role context. I wont
even speculate on what that should/could/would do, because I think we
just shouldn't go there.
We talked briefly about considering this "flattening" as being a
run-time state thing rather than necessarily taking place in the
Meta-objects. Is this still tenable?
Well if the MetaClass is where all methods are stored, then it will
need to be "meta" in some way. As for run-time vs. compile-time, that
makes no difference. It happens at class composition time, which can
take place at any (run|compile)time.
The strength of the role flattening is in the fact that, given it's
rules of composition, it makes the order in which the roles are
composed irrelevant. So as long as we flatten the same way, it does not
matter.
My point was more that we need to keep the same flattening rules, and
not let
Side effects like what happens if you do "higher level" stuff like
run-time modification of Roles after they have been composed into
classes can be addressed seperately.
Well, this was something I think we talked about. Basically my position
is that once you compose a role into a class, the connection the class
retains to that role is only minimal. Basically enough to handle "does"
and nothing more. If you modify the Role at runtime, it will not affect
and previous classes it was composed into.
Personally I don't like the idea of runtime class modification, I think
it is a dangerous thing to do. But I understand that it is a useful
feature in certain cases. But runtime Role modification I think is just
insane, especially since roles can be used by multiple classes, and
combined with multiple other roles. The combinations of conflicts and
issues that can arise are endless. Not to mention the fact that given a
widely enough used role, it would be practically impossible to reason
about the affect of changing that role.
I think if you find that you need to modify your role at runtime, then
you have actually found that you need a class, and not a role.
No, that's correct. We've just basically said that $.x is now a
method
call, and as long as you stick to that invocation syntax it just has
to be declared as a method somewhere in the parentage/role-age of
this object. But collisions in $.x declaration will be treated as
collisions in method declaration.
So we really are looking at closure based access to attributes. This
also means that the idea behind P6Opaque as a storage format is very
different. In theory, we could implement these style classes in a
very "Inside-Out" manner. In a way, we could look at:
has $.x is rw;
as being sugar for this (pseudo) Perl 6:
{
my %x;
$?CLASS.meta.add_method('x' => method ($self: ?$value) {
%x{$self.id} = $value if $value.defined;
%x{$self.id};
});
}
Yes, precisely ... except the method returns a Proxy object.
The proxy object responds differently depending on its context.
Sure, that makes sense.
To summarise with code for those who are lost;
has $.foo;
would be sugar for:
has $foo;
method foo is accessor( :FETCH { $foo },
:STORE -> $x { $foo = $x },
);
If you use \( $object.foo ) - that is, create a reference to the .foo
property, you get a Proxy object; the above should be considered
functionally equivalent to;
has $foo;
method foo is rw {
return Proxy.new(:FETCH { $foo },
:STORE -> $x { $foo = $x },
);
}
This is looking a lot like properties in C#. Which look something like
this:
private String node;
public String Node {
get { return this.node; }
# NOTE: the 'value' variable is created for you
set { this.node = value; }
};
I think that is a good thing, I know I liked having properties when I
did some C3 work a little while back.
This is extended into the other sigil types;
has %.foo;
is sugar for this:
has Hash $foo; # or has %foo, but really, the point is it's
# an implementation detail, right?
method foo is rw {
return Proxy.new( :FETCH{ $foo }, # or a facade
:STORE -> Hash $x { $foo = $x },
)
but {
method post_circumfix:<{}>($key) is rw {
return Proxy.new( :FETCH{ $foo{$key} },
:STORE -> $x { $foo{$key} = $x },
);
}
};
}
I am not sure if it makes sense to handle whats inside the hash with
the accessor. I think that is the responsibility of either the hash
container type itself, or what you really need here is some kind of
object to wrap your hash and get such fine grained control. But that is
just my gut reaction.
Well, if we ditch the P6Opague type (as we currently know it) and use
the Inside-Out method I show above. Then we just don't actually have
class/instance attributes anymore at all. We really just have
methods, some of which happen to be closures around per-instance
values. Of course this means there is no direct access to attributes
at all (which I think it not a bad thing, but I am into B&D like
that).
So, if you want a "visitor pattern", you grab a visitor iterator
via the Meta-Objects, right? Which could also solve the
STORABLE_freeze/STORABLE_thaw / Pixie::Complicity / etc problem by
unifying the way that "foreign" objects can marshall themselves to
Perl objects when required.
You mean for persistence operations? Where you need to thoroughly
inspect the object to save it to disk/DB/etc.
I think the ideal way to write something like that would be to write is
as a metaobject. Meaning using the Persistent metaclass. As opposed to
using something on the "user" side of things to interogate the "meta"
side of things. But this may be because I am too deep into the meta
side if things right now.
Stevan
*sniff* *sniff* this is smelling better and better :)
Sorry, I'll put that out.
Sam.