On 2009-Oct-17, at 1:55 am, 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:)?

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...)


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;}

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.

OK; although it seems reasonable to have some sugar for the obvious kind of "keep them all" methods like in this example. In fact, we probably have that already, by declaring a "proto log" that makes the others all work according to their sigs. And my mistake was thinking that you could have the same sig doing different things, but really the second sig is log(Numeric Logging:).

(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.)

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

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.?)

But I could see other alternatives:
.log given Logging $x; # assumes the inclusion of a "given" statement modifier.

I think "given", as either a modifier or a block, is the "prettiest" syntax.

   $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.)

   $x.log:(Logging:);


And I like this way because it's the most compact, "inline" way to indicate it.


-David

Reply via email to