David Green wrote: > Jon Lang wrote: >> >> This implies that both Logging and Math do Numeric, since the invocant >> ought to be of a type that the class does. > > I would expect that > role Logging { method log(Numeric $x:) {...} } > means the invocant is really of type Numeric & Logging, without Logging > having to do Numeric. On the other hand, I can see that strictly that might > not make sense, so perhaps I really do need to create a compound NumLog type > first, so I can have method log(NumLog:)?
I think you should need to do this. > Or can I create a method outside of any role: > role Numeric {...} > role Logging {...} > method log(Numeric Logging $x:) {...} > > (of course, that might be implicitly creating an anonymous compound type for > me...) Last I checked, all methods must be members of a class or role. >> I think that what you're actually looking for (for the purpose of >> illustration) is Logging::log:(Numeric $x:) and Numeric::log:(Numeric $x:). > > Oh, yes! > >> 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 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.> >> >> 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. > > I suppose given that I want Logging's method log(Numeric Logging:) rather > than its log(Any Logging:), the second method there should really be: > method log(Numeric Logging $x:) {$x.Logging::log;} I suppose that that would work, too. > (The only way to have the same sig twice would be something like if Logging > defined a special version of log() for Numeric objects, while Numeric > defined a special log() for Logging objects -- but semantically that ought > to mean the same thing in both cases, so we do want a single method to > handle that.) And if you limit yourself to referencing types that the method's role does, this won't be an issue. >> 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 > > I also thought "given" sounded good for this, but it would have to work > differently from a normal "given": if $x doesn't do Logging, then it needs > to skip the block. (Also, it looks very close to casting: "given > Logging($x)". Maybe something a bit more visually distinctive would be > helpful, something like "given $x as Logging", etc.?) IMHO, "given $x { ... }" is effectively syntactic sugar for "$x -> $_ { ... }", and "given Numeric $x { ... }" would be syntactic sugar for "$x -> Numeric $_ { ... }". If $x doesn't do Numeric, the default behavior should be a fail. >> $x -> Numeric $n { ... ; $n.log ; ... } > > What I like about this is using a sig to apply the context, so no new syntax > is needed. > > (But I'll suggest something new for -> in general: what if "$x -> Numeric" > with no "$n" variable were shorthand for "$x -> Numeric $x is rw", i.e. a > shorthand that used the same variable name inside the block as the one being > passed in? That would be useful in cases like this where we don't > particularly want to rename $x.) It wouldn't always be workable; for instance, "@a -> Numeric, Stringy { ... } would grab the first two element of @a and would put them into parameters; but there would be no obvious names to assign to those parameters. -- Jonathan "Dataweaver" Lang