Front page | perl.perl6.language |
Postings from January 2004
RE: supply and demand (was: Roles and Mix-ins?)
Thread Previous
|
Thread Next
From:
Austin Hastings
Date:
January 7, 2004 08:16
Subject:
RE: supply and demand (was: Roles and Mix-ins?)
Message ID:
ICELKKFHGNOHCNCCCBKFIEEBCJAA.Austin_Hastings@Yahoo.com
From: Jonathan Lang [mailto:dataweaver42@yahoo.com]
> Austin Hastings wrote:
> > Jonathan Lang wrote:
> > > Austin Hastings wrote:
> > > > There's two ways to look at that. One way is to say: "I'm going to
> > > > define an interface as being this OTHER thing minus a method." That
> > > > seems like a positive construction, and supporting it might be
> > > > desirable.
> > > >
> > > > The other way is to say: "Nobody knows what methods call what other
> > > > methods in their implementation (nor should we know). Therefore,
> > > > removing methods is forbidden. If you have a conflict of methods,
> > > > alias them and provide support in the knowledge that any component
> > > > C<role> that requires the method may call it internally."
> > >
> > > Or you could say that when you "exclude" a method, what you're really
> > > doing is hiding it from everything external to where it's declared,
> > > while leaving it available to be called internally. Method exclusion
> > > would be more like declaring a private method in C++ than actually
> > > removing it from the class or role. This means that a method wouldn't
> > > be provided to a class that C<does> its role but excludes it itself,
> > > and thus it wouldn't be used to satisfy the requirements of any other
> > > roles that the class C<does>.
> >
> > Huh? If it's available to be called internally, you've got the same
> > problem (we were talking about conflict resolution earlier, I think) --
> > that you need to know which one to call.
> >
> > But if you say "there ain't no bark", either we should complain that
> > doing a Dog or a Tree demands it, or we should catch the exception at
> > compile or run time. I'm in favor of the first solution -- you must
> > provide one -- since that seems to be more in keeping with the general
> > "role philosophy" that Larry's been emitting.
>
> Ah; I think I see the confusion. You're conflating methods that a role
> supplies with methods that a role demands.
>
> Consider this:
>
> role Tree {
> method bark() {...};
> must grow(); # Tree doesn't have a grow method,
> # but its class will have to have one.
> };
>
> role Dog {
> method bark() {...};
> must grow();
> };
>
> class Trog does Tree does Dog {
> };
>
> my Trog $spot;
> $spot.bark; # what happens?
> $spot.grow; # what happens?
>
First, my understanding is that there's a shared requirement (must grow) and
a name conflict. Before this can compile, we'll need to provide a grow()
method, and we'll need to disambiguate the bark method: there can be only
one. Larry and the paper are pretty clear on this point.
So I'm thinking
class Trog does Tree does Dog { bark => dogBark } {
method grow {...};
}
> OK: when you call $spot.bark, Trog looks for a "bark" method; it finds
> two: Tree::bark and Dog::bark. Since both methods have been supplied by
> roles, $spot has no idea which one to use, and throws an exception to that
> effect.
I am uncomfortable with this. My understanding has been that the will be a
compile time error, caught when the class declaration is made. Has that
changed recently?
> When you call $spot.grow, however, Trog finds _no_ methods; Dog doesn't
> supply a grow method, and neither does Tree. You get a compile-time error
> to the effect of "incomplete class declaration - missing a grow method."
I sort-of agree. I don't think it requires a call to grow for the compile
time error -- I think it's a declaration time error. (I suppose that the
declarator could provide a "secret" {...} declaration for all required
methods, but what value does this have?)
> As such, the code given above is incorrect. Adjusting for this error, we
> rewrite Trog as follows:
>
> class Trog does Tree but no bark does Dog {
> method grow() {...};
> };
Modulo syntax, this is more right, yes.
> Now when we call $spot.bark, it finds Dog::bark but not Tree::bark;
> therefore, it barks like a dog.
Yep.
> When we call $spot.grow, it finds Trog::grow, but there's still no
> Dog::grow or Tree::grow for it to find; so it grows as only a Trog can.
Yep.
> Things get more interesting when one role demands a method and another
> role supplies it:
>
> role Tree {
> method bark;
> }
>
> role Dog {
> must bark;
> method threaten { bark; }
> }
>
> class Trog does Tree does Dog {
> }
>
> my Trog $spot;
> $spot.threaten; # what happens?
>
> When $spot.threaten is called, Trog only finds Dog::threaten, and thus
> calls it. Dog::threaten then calls $spot.bark; Trog finds a Tree::bark
> method and nothing else, so $spot barks like a Tree.
Right. This seems sensible.
> In other words, when a method supplied by a role calls another method,
> that call gets dispatched to whatever class that role is a part of.
>
> Combining this example with the first one:
>
> role Tree {
> method bark() {...};
> must grow(); # Tree doesn't have a grow method,
> # but its class will have to have one.
> };
>
> role Dog {
> method bark() {...};
> must grow();
> method threaten { bark; }
> };
>
> class Trog does Tree does Dog but no bark {
> method grow() {...};
> };
>
> my Trog $spot;
> $spot.threaten; # what happens?
>
> When $spot.threaten is called, Trog only finds a Dog::threaten() method,
> and dispatches the call to it; it in turn calls $spot.bark; Trog finds a
> Tree::bark but not a Dog::bark (because the bark that would normally be
> supplied by Dog was suppressed by Trog). There's no ambiguity, so $spot
> barks like a Tree.
Good so far.
> So what would happen in the following situation:
>
> role Dog {
> method bark() {...};
> method threaten { bark; }
> };
>
> class Toothless does Dog but no bark {
> }
>
> my Toothless $rover;
> $rover.threaten;
>
> $rover.threaten calls $rover.bark; #Toothless then looks for a bark
> method...
>
> ...and doesn't find one. Hmm...
>
> This is only a problem because one of Dog's methods calls the bark method;
> doing so implicitly causes the role to demand that method on a conceptual
> basis. Any attribute or method that is used by any of a role's supplied
> methods is implicitly demanded by that role; but the compiler cannot
> easily figure out which ones those are. OTOH, do we really want to assume
> that Dog demands threaten if none of Dog's other methods call it? It
> would in theory be safe to exclude "threaten" from Dog, even though it
> wouldn't be safe to exclude "bark".
I agree that this is the question.
> Perhaps _threaten_ can be made to explicitly demand access to bark? That
> would allow a role's demands to be composed from its methods in a manner
> similar to how a class' methods are composed from its roles... something
> like:
>
> role Dog {
> method bark() {...}
> method threaten must bark() { bark; }
> }
>
> class Toothless does Dog but no bark() {
> }
>
> would be illegal because
> Dog::threaten() must bark(), thus
> Dog must bark(), thus
> Toothless must bark(), but
> neither Toothless nor its role can bark().
>
> But
>
> role Dog {
> method bark() {...}
> method threaten must bark() { bark; }
> }
>
> class Toothless does Dog but no threaten() {
> }
>
> would be fine; even
>
> role Dog {
> method bark() {...}
> method threaten must bark() { bark; }
> }
>
> class Toothless does Dog but no threaten() no bark() {
> }
>
> would be fine (in this case, because removing threaten also removes the
> demand for bark).
And this seems to be a valid way to get around the problem.
> But in the case of
>
> role Dog {
> method bark() {...}
> must bark();
> method threaten must bark() { bark; }
> }
>
> class Toothless does Dog but no threaten() no bark() {
> }
>
> You'd still have a problem, because even excluding threaten doesn't
> exclude Dog's demand to be able to bark.
"Hit jist haint a dawg ifn hit dont bark."
But large roles are going to want to say C<must> just once, since (1) that
makes for easier, more legible documentation; and (2) if chromatic is
correct, most functionality with roles (smaller than classes, recall) will
likely be pretty incestuous.
So I'm still a little uncomfortable stripping bits out without replacing
them. I think I'd rather see a no-op instead.
=Austin
Thread Previous
|
Thread Next