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

Reply via email to