Perl 6 multi methods never match expected signature
Asked Answered
R

2

10

I have a class with two multi methods (multi submit).

I call my multi like this:

$perspective.submit(:message($message.content));

Which gets shipped off to my class:

my $perspective-api = API::Perspective.new(:api-key(%*ENV<PERSPECTIVE_API_KEY>));

proto method submit (|) {*}

multi method submit(Str :$message!, MODEL :@models = TOXICITY) {
    my $score = $perspective-api.analyze(:@models, :comment($message));
    say @models Z=> $score<attributeScores>{@models}.map: *<summaryScore><value>;

multi method submit(Str :$name!, MODEL :@models = TOXICITY) {
    my $score = $perspective-api.analyze(:@models, :comment($name));
    say @models Z=> $score<attributeScores>{@models}.map: *<summaryScore><value>;
}

However I always get the following response:

Died because of the exception:
    Cannot resolve caller AUTOGEN(Rose::ContentAnalysis::Perspective:D: :message(Str)); none of these signatures match:
        (Rose::ContentAnalysis::Perspective: Str :$message!, MODEL :@models = MODEL::TOXICITY, *%_)
        (Rose::ContentAnalysis::Perspective: Str :$name!, MODEL :@models = MODEL::TOXICITY, *%_)

Despite my named argument (:message) being a Str as required and @models having a default declared.

Rhythm answered 25/6, 2019 at 15:46 Comment(2)
Could you share the definition of TOXICITY? I'm wondering if your default value is impossible to assign.Tyishatyke
@JonathanWorthington you can find the MODEL enum within my API::Perspective module here, if that is what you are looking for. github.com/shuppet/p6-api-perspective/blob/master/lib/API/…Rhythm
T
12

Multiple dispatch works in two phases:

  • Considering the number of positional parameters and their types
  • If there are any where clauses, named parameters, or sub-signatures, doing a test bind of the signature to see if it would match

The second phase will reject the candidate if it fails to bind for any reason. One such reason, and I believe the cause of the issue here, is that the default value is wrongly typed. For example, in:

multi m(:@x = "not-an-array") { }
m()

We get an error:

Cannot resolve caller m(...); none of these signatures match:
    (:@x = "not-an-array")
  in block <unit> at -e line 1

But changing it to:

multi m(:@x = ["an-array"]) { }
m()

Works fine. (Note that while a default value uses =, it's actually a binding, not an assignment.)

In the case in the question there's this:

MODEL :@models = TOXICITY

Looking at the module source the code is taken from, I see:

enum MODEL is export (
        <TOXICITY SEVERE_TOXICITY TOXICITY_FAST IDENTITY_ATTACK
        INSULT PROFANITY SEXUALLY_EXPLICIT THREAT FLIRTATION
        ATTACK_ON_AUTHOR ATTACK_ON_COMMENTER INCOHERENT INFLAMMATORY
        LIKELY_TO_REJECT OBSCENE SPAM UNSUBSTANTIAL>
);

Thus TOXICITY is just an Int, but what's expected is a typed array of MODEL values.

Thus, if you do this:

multi method submit(Str :$message!, MODEL :@models = Array[MODEL](TOXICITY)) {

It should work.

Tyishatyke answered 25/6, 2019 at 16:1 Comment(0)
V
8

I see two issues.

One is that you have two methods that are identical except for the name of one named parameter.

Named parameters can have aliases:

#                       V--------------V
multi method submit(Str :name(:$message)!, MODEL :@models = TOXICITY) {
    my $score = $perspective-api.analyze(:@models, :comment($message));
    say @models Z=> $score<attributeScores>{@models}.map: *<summaryScore><value>;
}

Note that :$message is really short for :message($message)


Now on the problem which actually prevents your code from working.

@models is a Positional, but you are assigning it a singular value in the signature.

Assign it a Positional, and it works:
(In this case it has to be of type Array[MODEL] because of the MODEL type declaration.)

#                                                           V---------------------V
multi method submit(Str :name(:$message)!, MODEL :@models = Array[MODEL](TOXICITY,)) {
    my $score = $perspective-api.analyze(:@models, :comment($message));
    say @models Z=> $score<attributeScores>{@models}.map: *<summaryScore><value>;
}
Venomous answered 25/6, 2019 at 16:2 Comment(3)
Thanks for your response Brad, Jonathan's solution actually solved my core issue but I think you did a good job pointing out code that is probably going to bite me further down the line so I'll take these suggestions into consideration too. :)Rhythm
@raiph I tried a much smaller subset of the problem when I was testing.Venomous
Hi Brad. But... Named parameter alias Are you saying your mention of the named parameter alias aspect should now be understood as purely about possibility for a reader of the code being confused? Or did you see an instance in which things went wrong in your tests? Or foresee a problem even though you didn't see one in your tests? Array[Model] Do you concur that the key issue with the OP is that a Bar @baz parameter or similar must be bound to a Foo[Bar] argument, where Foo does Positional? Perhaps the answers to my questions are obvious to some but not to me. TIA for a reply.Horde

© 2022 - 2024 — McMap. All rights reserved.