Jon Lang wrote:
Jonathan Worthington<jonat...@jnthn.net> wrote:
Ovid wrote:
Though I have issues with Jonathan's approach (I don't like classes
silently discarding role methods as this has caused us many bugs at the
BBC), it's much cleaner that what I see here.
s/Jonathan's approach/Perl 6's approach/ # at least, so far as I understand
Perl 6
No; I think he meant "Jonathan's approach", as in the suggestion that
I made earlier about disambiguating by exclusion.
Oh, sorry. ETOOMANYJONATHANS. ;-)
It occurs to me that "but" might be a way to approach this, since the
linguistic purpose of that operator is to generate a class or role
that is a slight departure from an existing role:
A but without(&foo)
might generate an anonymous role that's exactly like A except that it
doesn't have the foo method; you could then say something like:
role A { method foo() { }; method bar() { }; method baz() { } }
role B { method foo() { }; method bar() { }; method baz () { } }
class C does A but without(foo, bar) does B but without(baz) { }
But C<but> composes in a role to a copy of the object, so it's not
really following the semantics we want here, and also means we need to
make without mean something special other than just being a role name.
And we'd have to tinker quite a bit with the way trait_mod:<does> parses
to make it handle this. I guess we could do that somehow, but it feels
odd. Also, trying to look up &foo - which would be in a completely
separate role - is going to be odd too. It feels like we're making far
too many things mean something a bit different.
More fitting to me would be an adverb to the does trait modifier...
class C does R1 :without<foo bar> does R2 :without<baz> { ... }
The thing is that in this case, does the class actually do R1 and R2? If
you are going to derive an anonymous role with the methods missing, then
answer is "no". That is, C ~~ R1 would be false. So I think it'd need to
act as a modifier to the action of composition, rather than a
modification to the thing that we're composing.
I agree that there are dangers involved in excluding methods from a
role; thus my use of fairly lengthy phrases in order to accomplish it
- a sort of "handle with care" warning.
I wonder if we'd want to mandate that a method of the name must come
from _somewhere_ otherwise it's an error. At least then you get a
promise that a method of that name exists...which is about all that "it
does this role" tells you as an interface contract anyway.
Alternatively, I could see a version of this exclusionary policy being
done through method delegation, by means of the whatever splat -
something like:
class C {
has A $a handles * - (foo, bar);
has B $b handles * - baz;
}
The RHS of the handles is something we smart-match the method name
against (unless it's one of the special syntactic cases). And thus if
you care about performance you probably don't want to be falling back to
handles to do your role composition, since it's kind of the "last
resort" after we've walked the MRO and found nothing. Anyway, you'd put
something on the RHS maybe like:
has A $a handles none(<foo bar>)
But I'm not sure that will fall through to B for anything that A doesn't
define other than those two. You'd perhaps just get a dispatch error if
you said A handles everything but those and it didn't. So it'd probably
look more like...
has A $.a handles all(any(A.^methods>>.name), none(<foo bar>));
Which you almost certainly don't want to be writing. ;-)
Jonathan