in reply to Docs notation

Perl does not have positional empty parameters:

foo('bar', , 'baz')

is exactly the same to Perl as

foo('bar', 'baz')

and that's why I definitively tend towards the last documentation:

$ret = foo($scalar, [$arrayref, [$hashref]]);

If you have behaviour that can determine whether the second parameter was left out, as in your example by the type of reference passed in, just document that in the prose:

If the second parameter is a hash reference and no third parameter is passed in, it is interpreted as the third parameter and the second parameter defaults to [...]

Of course if there is no "natural" order of parameters, consider passing named parameters as a hash:

# The naming is obviously somewhat abstract here foo( scalar => $scalar, hashref => {...}, things => \@things );

This has the advantage that you can add new options/parameters later without worrying about the existing code.