Constraints

Candidates can specify more complex signatures:

    multi to-json($d where {!defined $d}) { 'null' }

This candidate adds two new twists. It contains no type definition, in which case the type of the parameter defaults to Any, the root of the normal branch of the type hierarchy. More interestingly, the where {!defined $d} clause is a constraint, which defines a so-called subset type. This candidate will match only some values of the type Any--those where the value is undefined.

Whenever the compiler performs a type check on the parameter $d, it first checks the nominal type (here, Any). If that check succeeds, it calls the code block. The entire type check can only succeed if the code block returns a true value.

The curly braces for the constraint can contain arbitrary code. You can abuse this to count how often a type check occurs:

    my $counter = 0;

    multi a(Int $x)  { };
    multi a($x)      { }
    multi a($x where { $counter++; True }) { };

    a(3);
    say $counter;       # says B<0>
    a('str');
    say $counter;       # says B<2>

This code defines three multis, one of which increases a counter whenever its where clause executes. Any Perl 6 compiler is free to optimize away type checks it knows will succeed. In the current Rakudo implementation, the second line with say will print a higher number than the first.

In the first call of a(3), the nominal types alone already determine the best candidate match, so the where block never executes and the first $counter output is always 0.

The output after the second call is at least 1. The compiler has to execute the where-block at least once to check if the third candidate is the best match, but the specification does not require the minimal possible number of runs. This is illustrated in the second $counter output. The specific implementation used to run this test actually executes the where-block twice. Keep in mind that the number of times the subtype checks blocks execute is specific to any particular implementation of Perl 6.