David Green wrote: > Aha, so the bark:(Dog:) syntax identifies the method by its signature as > well, thus distinguishing it from the .bark:(Tree:) method. This works fine > when the sigs can distinguish the invocants, which is very common. However, > I could have ambiguous methods even including the signatures. Suppose I > have a Logging role that provides a log() method for printing some info > about a variable. In particular, I have method log(Numeric $x:) { ... } > because I want to handle Nums specially (say, round them off before > printing). Meanwhile, suppose I also have Math::log(Numeric $x:).
So you have Logging::log:(Numeric $x:), and you have Math::log:(Numeric $x:). This implies that both Logging and Math do Numeric, since the invocant ought to be of a type that the class does. (And incidentally, this brings up another issue: as written, Math isn't a class; it's a module. Modules generally don't do roles, assuming that they even can.) Note further that in the setting, you actually have Math::log:(Numeric $x). Modules usually don't have methods, and so their routines generally don't have invocants. I think that what you're actually looking for (for the purpose of illustration) is Logging::log:(Numeric $x:) and Numeric::log:(Numeric $x:). Continuing on with that: > If $x does Numeric and does Logging, then $x.log won't be able to decide > which method to call, unless maybe it's in a sub like foo(Numeric $x) that > can know to provide "Numeric" context to $x. If $x does Numeric and $x does Logging, then it has a class that has already encountered the potential conflict and resolved it in some way. For example: class Foo does Numeric does Logging { method log(Numeric $x:) {$x.Numeric::log;} } # Foo picks out the method from Numeric. class Bar does Numeric does Logging { method log(Numeric $x:) {$x.Logging::log;} } # Bar picks out the method from Logging. class Baz does Numeric does Logging { method log(Numeric $x:) {$x.Numeric::log;} method log(Logging $x:) {$x.Logging::log;} } #`<Baz postpones the decision until it knows which role it's being asked to play: Numeric or Logging.> If $x is a Foo, then $x.log will always behave like Numeric::log; if $x is a Bar, then $x.log will always behave like Logging::log. Baz illustrates my proposal: if $x is a Baz, it will need to check the context to see if it's supposed to be acting like a Numeric or like a Logging, and will act accordingly - or it will complain about ambiguity if it can't figure out which role to play. And the definition for Baz works because Logging does Numeric. You cannot define a class that does Logging and does Numeric without defining at least one log method, because they conflict; and a class must somehow resolve all such conflicts. > Outside foo, or inside a sub > like bar(Any $x), I need some other way to indicate which "log" method I > mean. $x.log:(Numeric:) won't work here, because both roles provide a > method with that name and signature. As I indicated above, it will work, because $x.WHAT will have addressed the matter already. In the Foo and Bar cases, it addresses the matter by picking one or the other and preventing access to the one it doesn't pick; this is a viable stratagem if Logging and Numeric are semantically similar (and, seeing as how Logging does Numeric, they probably are). In the Baz case, it addresses the matter by making two options available according to the role being played: Numeric or Logging. All you have to do then is to somehow indicate which role is being played. If you can't tell by the routine's signature, my own preference would be to make it explicit by means of a "given" block: given Logging $x { .log } # logs like a Logging given Numeric $x { .log } # logs like a Numeric But I could see other alternatives: .log given Logging $x; # assumes the inclusion of a "given" statement modifier. $x -> Numeric $n { ... ; $n.log ; ... } $x.log:(Logging:); The point is that you're never going to have two different &log:(Numeric:) methods in the same class. -- Jonathan "Dataweaver" Lang