chenyf
chenyf

Reputation: 5058

How to get all the signatures of multi sub or build-ins?

I defined a multi sub which has two signatures:

multi sub mie(Str $s, Int $i) { $s x $i }
multi sub mie(Int $s, Int $i) { ... }
say &mie.signature; # ;; Mu | is raw)

I want to get the signature of this multi sub, but the above result is not what i expected.

As the document said, contains is a multi method which has 4 signatures:

multi method contains(Str:D: Cool:D $needle)
multi method contains(Str:D: Str:D $needle)
multi method contains(Str:D: Cool:D $needle, Int(Cool:D) $pos)
multi method contains(Str:D: Str:D $needle, Int:D $pos)

But when i try to get the signature of contains:

say "a string".^methods.pairs.values[8].value.signature; 

It only output one signature:

(Str: | is raw)

In the REPL, when i call the contains method without argument, it output the following error:

> "a string".contains()
Cannot resolve caller contains(Str: ); none of these signatures match:
    (Str:D: Cool:D $needle, *%_)
    (Str:D: Str:D $needle, *%_)
    (Str:D: Cool:D $needle, Cool:D $pos, *%_)
    (Str:D: Str:D $needle, Int:D $pos, *%_)
  in block <unit> at <unknown file> line 1

That indicate that contains method indeed has 4 signatures! I want to know is there any methods that can output all the signature of a method/multi method?

Upvotes: 13

Views: 209

Answers (3)

raiph
raiph

Reputation: 32489

As a complement to Curt's answer:

proto foo (;; Mu | is raw) {*}               # proto(;; Mu | is raw)

multi foo ($a)             { }               # multi($a)
multi foo (Int $a)         { }               # multi(Int $a)

multi foo ($a,$b)          { }               # multi($a, $b)
multi foo (Int $a,$b)      { }               # multi(Int $a, $b)

say 'proto', .signature for &foo;            # displays 1 line
say 'multi', .signature for &foo.candidates; # displays 4 lines

I've shown the results of the says alongside their respective routines.

If you call foo ... where foo is a multiple dispatch routine (same with .foo) then, at least semantically, you're actually calling a proto declared for that name that then (normally) redispatches to the best fitting multi with the same name.

(If you call methods on &foo, rather than foo, then you're calling them on the Routine object for that proto.)

A manually declared proto provides complete control of the dispatch process. It can make a cup of tea and then use Common Lisp dispatch semantics and then fiddle with the result. Or whatever else it wants to do.

If one or more multis are declared without explicitly declaring a proto then a default proto is automatically generated. I've manually declared a proto to ground the following:

  • The ;; in the signature excludes parameters in the proto sig from being relevant for "winning" the initial dispatch to it -- no matter what, the proto "wins" the initial dispatch and gets all the arguments;

  • The Mu parameter type constraint explicitly gives the broadest possible type to the | parameter, which parameter binds to the entire list of arguments passed to it (in a Capture), and the is raw means the capture is received in its "raw" form (I currently don't understand what this trait does here; I had thought a Capture is by definition a raw, unprocessed list of arguments).

  • The body then dispatches to the next same named routine (that's what the {*} does).

Upvotes: 11

cowbaymoo
cowbaymoo

Reputation: 1202

I made the P6Repl::Helper module a while ago for this purpose.

Upvotes: 2

Curt Tilmes
Curt Tilmes

Reputation: 3045

Try "a string".^lookup('contains').candidates».signature

.^lookup('contains') will find the Method

.candidates will list the multi candidates

.signature will give you the Signature for each one.

Output: ((Str:D: Cool:D $needle, *%_) (Str:D: Str:D $needle, *%_) (Str:D: Cool:D $needle, Cool:D $pos, *%_) (Str:D: Str:D $needle, Int:D $pos, *%_))

You can use it for your multi sub too:

say &mie.candidates».signature;

Upvotes: 11

Related Questions