Prototypes - Page 14
July 13, 2001
The subroutines we have considered so far exert no control over
what arguments are passed to them; they simply try to make sense
of what is passed inside the subroutine. For many subroutines
this is fine, and in some cases allows us to create subroutines
that can be called in a variety of different ways. For example,
we can test the first argument to see if it is a reference or
not, and alter our behavior accordingly. However, we are not
enforcing a calling convention, so we will only discover our
subroutines are being called incorrectly when we actually execute
the call, and then only if we have written the subroutine to
check its arguments thoroughly. Since some subroutine calls may
not occur except under very specific circumstances, this makes
testing and eliminating bugs very difficult.
Fortunately there is a way to define compile-time restrictions on
the use of subroutines through the use of prototype definitions.
Although entirely optional, by specifying the types of the
expected parameters, prototypes can eliminate a lot of the
problems involved in ensuring that subroutines are called
correctly. This allows us to specify what parameters a subroutine
takes (scalars, lists/hashes, or code references), and whether a
parameter can be either a simple literal value, or whether it
must be an actual variable. Good use of prototypes early in the
development process can be invaluable.
A prototype definition is a parenthesized list of characters
mirroring the Perl variable type syntax (that is, $,
@, %, and so on). It is placed after
the sub keyword and subroutine name but before anything else, be
it a subroutine definition, declaration, or anonymous subroutine:
sub mysub (PROTOTYPE); # subroutine declaration
sub mysub (PROTOTYPE) {...} # subroutine definition
$subref = sub (PROTOTYPE) {...} # anonymous subroutine
Defining the Number of Parameters and Their Scope
Prototypes allow us to explicitly define how many arguments a
subroutine expects to receive. This is something that for
efficiency reasons we would clearly prefer to check at compile
time. We do not have to wait until the subroutine call is used to
find out that it is faulty, and passing the wrong number of
parameters is an obvious candidate for a bug.
To illustrate, consider the volume subroutine that
we defined in various different forms earlier. With the exception
of the named argument example, the subroutine expects three
scalar parameters. Using prototypes we can enforce this by adding
($$$), meaning three mandatory scalar arguments, to
the subroutine definition:
sub volume ($$$) {
# ... as before ...
}
With this prototype in place, volume can only be
called with three scalar arguments. They can be literals or
variables, but there must be three of them, and they must be
scalar. Hence, this is legal:
print volume(1, 4, 9), "\n"; # displays 1 * 4 * 9 == 36
This, however, is not. Even though it provides the right number
of values, it doesn't supply them in a way that fits the
prototype:
@size = (1, 4, 9);
print volume(@size), "\n";
Instead, we get the error:
Not enough arguments
for main::volume at ... near @size
So far, so good. However, due to Perl's concept of context,
prototypes do not enforce things quite as strictly as this might
imply. The prototype does not actually enforce a data type
— it attempts to force it. What the first $ in the
prototype actually does is force @size to be
interpreted in scalar context and not as a list, in other words,
it is exactly as if we had written:
print volume(scalar @size), "\n";
Having turned the three element array into a scalar '3', the
prototype goes on to interpret the second argument as a scalar
also. It then finds there isn't one, and produces an error. The
fact that we passed an array is not relevant, since an array can
be converted to a scalar. However, by passing just one array, we
omitted two mandatory arguments, which is important. To
illustrate this, the following actually works just fine, the
array not withstanding:
print volume(@size, 4, 9); # displays 3 * 4 * 9 == 108
We have not supplied three scalars, but we have supplied three
values that can be interpreted as scalars, and that's what counts
to Perl.
Named Parameters: Additional Information - Page 13
Professional Perl Programming
Parameters (Continued) - Page 15
|