How do I specify a Perl 6 signature that covers everything I accept and excludes everything else?

My assumption in this matter is that the signature that I indicate fully describes everything that the method will accept. Apparently, I am mistaken in this, but still I would like to receive it. If I do not indicate it in the signature, I do not want other people to be able to provide it as an argument, thinking that he will do something.

I am creating a signature with a single positional parameter for the method, and I expect it to accept only one positional argument. However, it also accepts named parameters without complaint:

class SomeClass { method something ( Int $n ) { 'Ran something' } } put SomeClass.something: 137; # Ran something put SomeClass.something: 137, :foo('bar'); # Ran something 

But if I define a method that takes a named parameter that is called every time I define it second. Also, although I think I said that it requires a named parameter foo , it is not needed, and it still accepts named parameters that I did not specify:

 class SomeClass { multi method something ( Int $n ) { 'Ran something' } multi method something ( Int $n, :$foo ) { "Ran $foo" } } put SomeClass.something: 137; # Ran put SomeClass.something: 137, :foo('bar'); # Ran bar put SomeClass.something: 137, :bar('foo'); # Ran 

So, some questions:

  • How to specify a signature that covers everything that I want to accept and excludes everything else?

  • How to get Perl 6 to choose the closest comparable signature?

  • In what order did Perl 6 decide to test methods?

+7
perl6 signature
source share
3 answers

You are concerned with one of the things that are most difficult to understand about the delivery of several methods. The most important thing to understand is that each method signature has an implicit *%_ (aka slurpy hash) if it is not specified. This means that it will use any non-specific named parameters.

 class A { method a() { dd %_ } # %_ exists even if not specifically specified } Aa(:foo) # {:foo} 

Secondly, you need to understand that named parameters only act as tiebreaks. Therefore, if there are several candidates that have the same set of positional parameters, MMD will use the first one that works (given that all unexpected named parameters overlap %_ ):

 class A { multi method a(:$foo) { say "foo" } multi method a(:$bar) { say "bar" } } Aa(:bar) # foo 

This seemingly unexpected result is due to the fact that:

  • both candidates have the same number of positional parameters
  • first candidate matches because :$foo is optional
  • a :bar - implicit *%_

To make it work more, as you expect, you need to put in candidates who need to be tied to the order you want them to shoot and make any named parameters mandatory:

 class A { multi method a(:$foo!) { say "foo" } multi method a(:$bar!) { say "bar" } } Aa(:bar) # bar 

If you have candidates that accept multiple named parameters, they quickly become quite complex, and you might be better off just using a method that uses the introspection %_ :

 class A { method a() { if %_<foo> { say "foo" } elsif %_<bar> { say "bar" } else { die "None of the signatures matched" } } } Aa(:bar) # bar 

Hope this makes it easier :-)

+10
source share

Methods always have an implicit parameter *%_ , cf

 say method {}.signature #=> (Mu $: *%_) 

This is by design with the idea that subclasses can choose the arguments they need and then rewrite them through nextsame et al (cf design documents - I don’t know where or even if it is documented elsewhere).

There are several ways to reject undeclared named arguments, for example using the where clause

 method m($positional, :$explicit, *% where !*) { ... } 

or through empty tension

 method m($positional, :$explicit, *% ()) { ... } 

When passing an undeclared argument, the first failure will be

 Constraint type check failed in binding to parameter '<anon>' 

and the last one with

 Unexpected named argument '...' passed in sub-signature 

I will leave the exact semantics of sending a multimethod to others, but my heuristic rule is to make sure that the required named parameters are declared as such (i.e. present ! ) And put the more general last methods.

+5
source share

I think Int $n, :$foo more specific signature than Int $n . You can make foo mandatory

 class SomeClass { multi method something ( Int $n, ) { "Ran something" } multi method something ( Int $n, :$foo! ) { "Ran $foo" } } 

or the first signature is more stringent

 class SomeClass { multi method something ( Int $n, *% where "foo" !~~ *) { "Ran something" } multi method something ( Int $n, :$foo ) { "Ran $foo" } } 
+1
source share

All Articles