NAME
Sub::Declaration - declare subs with named parameters
SYNOPSIS
use Sub::Declaration;
sub mysub($scalar, \@array,) {
$array[$scalar] += $_ foreach (@_);
}
method mymethod($scalar, %hash) {
print $hash{$scalar}, "\n";
}
DESCRIPTION
Sub::Declaration
has three purposes:
Allowing subroutines to be declared and defined with a named list of arguments, as in most familiar programming languages.
Creating aliases of these parameters to the appropriate values.
Allowing methods to be created with automatic declaration of the variable containing the object reference.
For example, the "standard" way to write the example in the "SYNOPSIS" is something like
sub mysub ($\@@) {
my ($scalar, $arrayref) = splice @_, 0, 2;
$arrayref->[$scalar] += $_ foreach (@_);
}
The variable $arrayref
is simply a reference to an array, and you must remember to appropriately dereference it where necessary. By creating a variable named @array
that is aliased to the equivalent of @$arrayref
, you can avoid the need to dereference (Perl6::Binding is a way to create lexical aliases explicitly):
use Perl6::Binding;
sub mysub ($\@@) {
my ($scalar, $arrayref) = splice @_, 0, 2;
my @array := @$arrayref;
$array[$scalar] += $_ foreach (@_);
}
This module combines the extraction from the argument array and the creation of the alias into one operation.
Why Another Module?
There are already several modules for handling named subroutine parameters, such as Perl6::Parameters, Sub::Parameters, and Sub::NamedParams. So why another one?
The following reasons:
I wanted to implement the automatic aliasing of the parameters to lexical variables, in order to allow Perl's standard pass-by-reference to be used when necessary. In particular, I wanted to be able to specify that an array or hash reference is expected, and set things up so it is not necessary to dereference each use of the array or hash.
I wanted an unobtrusive syntax for declaring the parameters. If this were all I wanted I'd just have used Perl6::Parameters. The syntax for specifying parameters in the other modules just struck me as rather clunky.
I wanted to be able to declare methods and have the "$self" variable automatically created and populated.
DECLARATIONS
All declarations use the items specified in "ARGUMENT LISTS" below.
- sub
-
sub name(argument list) { ... }
The argument list is just a list of variable names, separated by commas. When the subroutine is called, these variables are associated with the items in
@_
. - method
-
method name(argument list) { ... }
This is useful to declare the methods in a class. It is identical to the "sub" declaration, but inserts a variable named
$self
into the front of the parameter list, which is set to the object reference at runtime.Thus, the declaration
method something(@parameters) { ... }
is expanded into
sub something($self, @parameters) : method { ... }
and then the argument list is processed.
No prototype is generated for a method declaration, since prototypes aren't honored for method calls anyway.
- argument list
-
argument list(argument list);
Sometimes the first few arguments to a function give the context to decode the rest of the arguments. For example, consider the following skeleton:
sub progress { my $type = shift; if ($type eq 'message') { my ($text) = @_; ... } elsif ($type eq 'progress') { my ($total, $completed) = @_; ... } }
This defines a subroutine that is called either as
progress('message', $text)
orprogress('progress', $total, $completed)
. The way to do this usingSub::Declaration
is like this:sub progress($type,) { if ($type eq 'message') { argument list ($text); ... } elsif ($type eq 'progress') { argument list ($total, $completed); } }
The argument list directive works at both compile time and runtime. At compile time it sets up the appropriate lexical variables. At runtime it populates them from
@_
just as it would as if they had been named in the original argument list. Any arguments not used are left in@_
as well, so there can be multiple rounds of argument decoding; you can even do something like this:sub pairs($function,) { ## do something with $function while (@_) { argument list($name, $value); ## do something with $name and $value } }
While in this case it probably doesn't buy you anything over
my ($name, $value) = splice @_, 0, 2
, there may be cases where the ability to modify the original value in place or create aliases to referenced arrays or hashes is important.You can use the argument list directive even if you don't supply any named parameters for a subroutine.
ARGUMENT LISTS
An argument list is just a list of variable names, separated by commas. A semicolon may appear in the list instead of one of the commas. This has no effect on the subroutine's code, but affects the generated prototype. Everything after the semicolon is optional.
There is some additional syntax that can be used with variable names, as noted in this list. $n
is the index with @_
for the named parameter. This table summarizes the various forms, which are explained in detail below.
Specification Variable Mode Source Prototype
------------- -------- ----- --------------- ---------
$name $name alias $_[n] $
COPY $name $name copy $_[n] $
\$name $name alias $$_[n] \$
@name @name slurp @{$_[n .. $#_]} @
COPY @name @name copy @{$_[n]} \@
\@name @name alias @{$_[n]} \@
%name %name slurp @{$_[n .. $#_]} %
COPY %name %name copy %{$_[n]} \%
\%name %name alias %{$_[n]} \%
&name $name copy $_[n] &
\&name $name copy $_[n] \&
*name $name copy $_[n] *
\*name $name copy $_[n] \*
$name
-
Unadorned scalars represent an alias to the corresponding in
@_
, which in turn is an alias back to the passed value. Modifying such a scalar variable causes the original passed value to be modified, or an error to be generated if the value isn't modifiable.Generates
$name
aliased to$_[$n]
, and a prototype of$
. COPY $name
-
A scalar preceded by
COPY
indicates that a copy of the scalar is to be used rather than an alias. You cannot change the original value through this parameter.Generates
$name
initialized with$_[$n]
, and a prototype of$
. \$name
-
A reference to a scalar requires
$_[$n]
to be a reference to a scalar. The corresponding actual parameter must begin with$
, and a reference to it is passed.Generates $name aliased to
$$_[$n]
, and a prototype of\$
. @name
%name
-
Unadorned arrays or hashes slurp up the rest of the argument list.
Generates
@name
or%name
initialized with@_[$n .. $#_]
, and a prototype of@
or%
. \@name
\%name
-
Preceding an array or hash with a backslash indicates that the single corresponding value must be a reference of the appropriate type.
Generates
@name
or%name
aliased to@{$_[$n]}
or%{$_[$n]}
, and a prototype of\@
or\%
. COPY @name
COPY %name
-
Preceding an array or hash with
COPY
creates a new lexical array or hash which is filled with a (shallow) copy of the referenced array or hash. This can simplify the expression of some algorithms, but you should avoid it with large arrays or hashes.Generates
@name
or@name
initialized with the contents of@{$_[$n]}
or%{$_[$n]}
, and a prototype of\@
or\%
. &name
-
Indicates that the corresponding value must be a code reference. If this is the first argument, the code reference doesn't need a leading sub or trailing comma.
Since Perl does not have lexical subroutines, generates $name. You will have to explicitly dereference this. The associated prototype is
&
. \&name
-
Almost the same as above, but the prototype is
\&
, which requires a subroutine name beginning with&
in this slot. *name
-
Indicates that the corresponding value must be a bareword, constant, scalar expression, typeglob, or a reference to a typeglob. Since there are no lexical typeglobs, generates
$name
containing a reference to a typeglob. The prototype is*
. \*name
-
Requires a reference to a typeglob. Generates $name (containing a typeglob reference) and a prototype of
\*
.
A trailing comma at the end of the argument list causes the generated prototype to have an extra @
at the end, but otherwise has no effect. Use this if you have a few fixed parameters followed by a variable number of items, and wish to leave the variable items in @_
rather than copying them into another array or hash.
OPTIONS
The following options may be specified when use'ing Sub::Declaration. You can change their values at any point in the program by re-use'ing the module with the new values of any options you want to change. (You will probably need to turn it off with no Sub::Declaration;
first, though.)
-noproto
-
Turns off prototype generation.
-proto
-
Turns on prototype generation. This is the default unless you've changed it.
-nomethod
-
Turns off the generation of the
method
attribute for method declarations. -method
-
Turns on the generation of the
method
attribute for method declarations. This is the default unless you've changed it. -nodebug
-
Turns off debugging output. This is the default unless you've previously turned it on.
-debug
-
Turns on debugging output, which causes the generated code to be output to standard output as it is filtered.
-self:name
-
Specifies that the object reference for methods should be set to
$name
rather than to$self
.
NOTES
As currently implemented, this module will expand subroutine declarations wherever they occur, even in strings or comments. You can prefix
sub
,method
, orargument list
with a backslash to turn off the expansion for that particular instance; the backslash is removed in the process of filtering the source code.
IMPLEMENTATION DETAILS
This module is implemented as a source filter (see Filter::Simple). It works by looking at all sub declarations with something that resembles an argument list as described above, and outputting the appropriate code.
For example, the following fragment from the "SYNOPSIS":
sub mysub($scalar, \@array,) {
$array[$scalar] += $_ foreach (@_);
}
actually gets rewritten as something similar to
sub mysub($\@@) {
my ($scalar, @array);
push @_, 1; &Sub::Declaration::arglist;
$array[$scalar] += $_ foreach (@_);
}
The 1
in the above example could be any integer; each argument list directive in the program is assigned a unique integer ID, which is used instead of generating instructions to pass the entire argument list definition each time.
The arglist Routine
Part of the generated code for a subroutine with named parameters calls the arglist subroutine within this module. This must be called using the &
-form of subroutine call with no arguments and no trailing parentheses, after pushing a reference to an array containing the names of the variables in the argument list. There is almost certainly no reason whatsoever for you to have to call this routine explicitly, so it is not documented further.
MODULES USED
Filter::Simple, Lexical::Util (version 0.8 or later)
SEE ALSO
For other approaches, see:
Perl6::Parameters, Sub::Parameters, Sub::NamedParams
COPYRIGHT AND LICENSE
Copyright 2004 Kevin Michael Vail
This program is free software. It may be copied and/or redistributed under the same terms as Perl itself.
AUTHOR
Kevin Michael Vail <kvail@cpan.org>