Multiple arguments

Candidate signatures may contain any number of positional and named arguments, both explicit and slurpy. However only positional parameters contribute to the narrowness of a match:

    # RAKUDO has problems with an enum here,
    # it answers with "Player One wins\nDraw\nDraw"
    # using separate classes would fix that,
    # but is not as pretty.
    enum Symbol <Rock Paper Scissors>;
    multi wins(Scissors $, Paper    $) { +1 }
    multi wins(Paper    $, Rock     $) { +1 }
    multi wins(Rock     $, Scissors $) { +1 }
    multi wins(::T      $, T        $) {  0 }
    multi wins(         $,          $) { -1 }

    sub play($a, $b) {
        given wins($a, $b) {
            when +1 { say 'Player One wins' }
            when  0 { say 'Draw'            }
            when -1 { say 'Player Two wins' }
        }
    }

    play(Scissors, Paper);
    play(Paper,    Paper);
    play(Rock,     Paper);

This example demonstrates how multiple dispatch can encapsulate all of the rules of a popular game. Both players independently select a symbol (rock, paper, or scissors). Scissors win against paper, paper wraps rock, and scissors can't cut rock, but go blunt trying. If both players select the same item, it's a draw.

The code creates a type for each possible symbol by declaring an enumerated type, or enum. For each combination of chosen symbols for which Player One wins there's a candidate of the form:

    multi wins(Scissors $, Paper $) { +1 }

Because the bodies of the subs here do not use the parameters, there's no reason to force the programmer to name them; they're anonymous parameters. A single $ in a signature identifies an anonymous scalar variable.

The fourth candidate, multi wins(::T $, T $) { 0 } uses ::T, which is a type capture (similar to generics or templates in other programming languages). It binds the nominal type of the first argument to T, which can then act as a type constraint. If you pass a Rock as the first argument, T acts as an alias for Rock inside the rest of the signature and the body of the routine. The signature (::T $, T $) will bind only two objects of the same type, or where the second is of a subtype of the first.

In this game, that fourth candidate matches only for two objects of the same type. The routine returns 0 to indicate a draw.

The final candidate is a fallback for the cases not covered yet--every case in which Player Two wins.

If the (Scissors, Paper) candidate matches the supplied argument list, it is two steps narrower than the (Any, Any) fallback, because both Scissors and Paper are direct subtypes of Any, so both contribute one step.

If the (::T, T) candidate matches, the type capture in the first parameter does not contribute any narrowness--it is not a constraint, after all. However T is a constraint for the second parameter which accounts for as many steps of narrowness as the number of inheritance steps between T and Any. Passing two Rocks means that ::T, T is one step narrower than Any, Any. A possible candidate:

    multi wins(Rock $, Rock $) {
        say "Two rocks? What is this, 20,000 years ago?"
    }

... would win against (::T, T).