On 2009-Jul-10, at 4:37 pm, Jon Lang wrote:
This is one of the distinctions between role composition and class inheritance. With class inheritance, the full tree of inherited classes is publicly accessible; with role composition, the methods of the combined role are the only ones that are made available to the class.


OK, that's actually about what I was thinking, despite the peculiar way I expressed it. I meant the full names to refer to methods directly in the composed role, not somewhere else. Of course, there's already a way to refer to methods with the same name -- using the long name that includes the signature. So my example should have used "bark(Canine: ...)" and "bark(Tree: ...)"; and whichever one actually gets called depends on whether the invocant does Canine or does Tree.

so Dogwood::bark ought to consider its context (am I being called to behave like a Canine, a Tree, or something else?) and decide what to do based on that. If Dogwood::bark isn't defined, you should get an implementation conflict error, because the class failed in its duty to provide an implementation.


Yes, and Dogwood::bark could handle it by something like: "if $self.does(Canine) {...} elsif $self.does(Tree) {...}" -- but Perl already knows how to handle multiple dispatch based on type, so I shouldn't have to write it out manually. In fact, this works with Rakudo: you can have both barks if you declare them as multis, and then it will accept them without having to declare a Dogwood::bark. (But of course if you try calling it, you get an "Ambiguous dispatch to multi 'bark'" error, because a $dogwood object equally satisfies both signatures.)

(I tried to see what would happen if you cast the $dogwood object to Canine or to Tree, but either Rakudo doesn't do it yet, or I got it wrong.)

Needing to say "multi" makes sense if you wanted multiple methods of the same name *within* a role (or class or any other namespace), but I don't think it should be necessary across different Roles. Since they already exist in different namespaces, we know they're supposed to mean different things, and it's a simple fact of life that sometimes the same term will get used in different places for completely different meanings. If you have to do the dispatching manually, I guess that's only a slight annoyance as long as it's possible. (Maybe it's better to force the programmer to do it, not because Perl couldn't, but to prevent potential surprises? Hm.)


   role R { method foo() { say "foo" }
   role R1 does R { method bar() { say "bar" }
   role R2 does R { method baz() { say "baz" }
   class C does R1 does R1 { }

The question is whether or not Rakudo is smart enough to realize that R1::foo is the same as R2::foo, or if it complains that R1 and R2 are both trying to supply implementations for foo. The former is the desired behavior.

Conversely, in this case the same name means the same thing, so it does seem perl ought to be able to tell that both foo's are really a single foo() here; since they both come from the same role (R), they have to mean the same thing, and C has to know that it does R.



In any case, then the question is how to know what role something does, which is really a question about casting and passing args rather than anything to do with Roles per se. I can't tell just from "$dogwood.bark" which kind of barking is wanted; but I could have Dogwood::bark_like_a_dog() instead, perhaps.

However, in
   sub nighttime (Canine $rover) { $rover.bark if any(burglars()); }

I can only call ".bark" because all I know for sure is that I have something which does Canine; if I pass it a $dogwood object, I see three possibilities:

1) $rover in the sub is just the Dogwood object that was passed in, and calling $rover.bark cannot know what to do. I also can't call $rover.bark_like_a_dog or anything else, because that method exists only for Dogwood objects, and the sub doesn't always receive Dogwoods. So I'm stuck, and I don't see any way around that the way things are.

2) $rover does Canine and only Canine -- the Tree-half of $dogwood that was passed in is invisible inside the sub, and thus $rover.bark calls bark(Canine:) which is what we want. (Of course, it calls Dogwood's bark(Canine:) when passed a Dogwood object -- it's not magically jumping back to the original Canine role.) If nighttime() in turn calls something-else($rover), the something-else sub also gets only a Canine object.

3) $rover acts like a Canine, but the rest of the original $dogwood arg (the Tree parts) are still there; they just aren't used unless somehow explicitly brought out; for example, by casting $rover to a Tree, or by passing it to some other function that is looking for a Tree object. This is how I'd like it to work, because that's the most flexible.

Maybe there should be "hard casting" and "soft casting": by hard casting I mean stripping out everything other than the requested Role/ Class, so that it's gone for good. I think that fits the usual idea of casting or coercing an object to a different type. Soft casting would be focussing on the requested Role and ignoring anything else the object does, but still leaving the other roles available if you really want them. Foo($x) would then do a "hard" cast, while passing $x to sub(Foo) would merely soft-cast it.


-David

Reply via email to