On Wed, Aug 20, 2008 at 3:16 PM, Aristotle Pagaltzis <[EMAIL PROTECTED]> wrote: > Hi $Larry et al, > > I brought this up as a question at YAPC::EU and found to my > surprise that no one seems to have thought of it yet. This is > the mail I said I'd write. (And apologies, Larry. :-) ) > > Consider the classic example of roles named Dog and Tree which > both have a `bark` method. Then there is a class that for some > inexplicable reason, assumes both roles. Maybe it is called > Mutant. This is standard fare so far: the class resolves the > conflict by renaming Dog's `bark` to `yap` and all is well. > > But now consider that Dog has a method `on_sound_heard` that > calls `bark`. > > You clearly don't want that to suddenly call Tree's `bark`. > > Unless, of course, you actually do. > > It therefore seems necessary to me to specify dispatch such that > method calls in the Dog role invoke the original Dog role methods > where such methods exist. There also needs to be a way for a > class that assumes a role to explicitly declare that it wants > to override that decision. Thus, by default, when you say that > Mutant does both Dog and Tree, Dog's methods do not silently > mutate their semantics. You can cause them to do so, but you > should have to ask for that. > > I am, as I mentioned initially, surprised that no one seems to > have considered this issue, because I always thought this is what > avoiding the False Cognate problem of mixins, as chromatic likes > to call it, ultimately implies at the deepest level: that roles > provide scope for their innards that preserves their identity and > integrity (unless, of course, you explicitly stick your hands in), > kind of like the safety that hygienic macros provide.
My thoughts: Much of the difficulty comes from the fact that Mutant [i]doesn't[/i] rename Dog::bark; it overrides it. That is, a conflict exists between Dog::bark and Tree::bark, so a class or role that composes both effectively gets one that automatically fails. You then create an explicit Mutant::bark method that overrides the conflicted one; in its body, you call the Tree::bark method (or the Dog::bark method, or both in sequence, or neither, or...) As such, there's no obvious link between Mutant::bark and Tree::bark. Likewise, you don't rename Dog::bark; you create a new Mutant::yap that calls Dog::bark. One thing that might help would be a trait for methods that tells us where it came from - that is, which - if any - of the composed methods it calls. For instance: role Mutant does Dog does Tree { method bark() was Tree::bark; method yap() was Dog::bark; } As I envision it, "was" sets things up so that you can query, e.g., Mutant::yap and find out that it's intended as a replacement for Dog::bark. Or you could ask the Mutant role for the method that replaces Dog::bark, and it would return Mutant::yap. It also provides a default code block that does nothing more than to call Dog::bark; unless you override this with your own code block, the result is that Mutant::yap behaves exactly like Dog::bark. By default, this is what other methods composed in from Dog do: they ask Mutant what Dog::bark is called these days, and then call that method. All that's left is to decide how to tell them to ask about Tree::bark instead, if that's what you want them to do. -- Jonathan "Dataweaver" Lang