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

Reply via email to