Jonathan Worthington wrote in "YAPC::EU and Perl 6 Roles":
> 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.

Moving the adverb from the role to the trait modifier would still
violate the notion that C ~~ R1; so it doesn't actually gain us
anything.  Instead, consider the following possibility: "R1
:without<foo bar>" is a separate but related role from "R1".  The
implicit relationship between them is that R1 ~~ R1 :without<foo bar>.
 So "C ~~ R1" would be false; but "C ~~ R1 :without<foo bar>" would be
true.

> 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.

Right.  Another way to handle this without establishing "reverse-does"
relationships that I describe above would be to say that the adverb
doesn't actually remove the method in question; it just suppresses its
implementation.  That is, given:

    role R1 {
        method foo() { say "foo" }
    }

    class C does :blocking<foo> R1 { ... }

this would compose R1 into C, but would discard the implementation of
R1::foo while doing so.

In the spirit of TIMTOWTDI, both approaches could be available:

"R1 :without<foo>" acts as an anonymous role that R1 implicitly does,
and which is set up almost exactly like R1 except that it doesn't have
method foo.  This could have other uses besides role composition, such
as expanding a web of roles from the specific to the general.

"C does :blocking<foo> R1" composes R1 into C, but discards foo's
implementation while doing so.

>> 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.

You're right, but for a different reason.  Perl 6 has three techniques
for code reuse, which I refer to as the "be, do, have" triad:

    Class inheritance ("to be"): 'is C;'
    Role composition ("to do"): 'does R;'
    Attribute delegation ("to have"): 'has A $a handles foo;'

Just on a conceptual level, you don't want to fall back on attribute
delegation in order to emulate role composition.  Still, there are
uses for delegating to all or most of an attribute's methods.

> 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. ;-)

Right; which is why I was looking for a more abbreviated form.  If we
go with the idea that we're using a Set on the RHS, then '*' could be
shorthand for 'Set($a.^methods)', and '&Set::infix:<->' could be the
Set Difference operator, with the item, list, or set on the RHS being
excluded from the LHS.  So:

    $a handles * - <foo bar>

would be short for something like:

    $a handles Set($a.^methods) - Set("foo", "bar")

This use of the Whatever splat is similar to its use in a list index,
where '*' behaves as if it were the list's element count.

--

Looking this over, I note that the only code reuse mechanism for which
we haven't looked at the "everything except..." concept is class
inheritance.  OTOH, I think that the blocking tool that works for role
composition could be adapted for use with class inheritence as well:

    Class C is :blocking<foo bar> C1 is :blocking<baz> C2 { ... }

That is, if you were to search the class heierarchy for foo, it would
skip the C1 branch in its search.  This would have to be treated with
care, because it would mean that C doesn't inherit everything from C1.
 This _would_ break the "anything you can do I can do, too" nature of
class inheritence; but I'm not sure how big of a crime that is.  Perl
generally discourages the use of class hierarchies, and it certainly
isn't the preferred means of type-checking.  And maybe you could get
around this by having the "isa" predicate return a more involved
response than merely "true" or "false" - say, an object that "is true"
but which can be queried for the exceptions:

    $test = C isa C1;
    if $test { say "they match!" } # output: "they match!"
    @exceptions = $test.blocking # one-item list: (C1:<foo bar>)

In short, "C isa C1" would answer "yes, except..."

-- 
Jonathan "Dataweaver" Lang

Reply via email to