From: Jonathan Lang [mailto:[EMAIL PROTECTED]
> 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

Reply via email to