Attribute::Default - Perl extension to assign default values to subroutine arguments |
Attribute::Default - Perl extension to assign default values to subroutine arguments
package MyPackage; use base 'Attribute::Default';
# Makes person's name default to "Jimmy" sub introduce : Default("Jimmy") { my ($name) = @_; print "My name is $name\n"; } # prints "My name is Jimmy" introduce();
# Make age default to 14, sex default to male sub vitals : Default({age => 14, sex => 'male'}) { my %vitals = @_; print "I'm $vitals{'sex'}, $vitals{'age'} years old, and am from $vitals{'location'}\n"; } # Prints "I'm male, 14 years old, and am from Schenectady" vitals(location => 'Schenectady');
You've probably seen it a thousand times: a subroutine begins with a
complex series of defined($blah) or $blah = 'fribble'
statements
designed to provide reasonable default values for optional
parameters. They work fine, but every once in a while one wishes that
perl 5 had a simple mechanism to provide default values to
subroutines.
This module attempts to provide that mechanism.
If you would like to have a subroutine that takes three parameters, but the second two should default to 'Mister Morton' and 'walked', you can declare it like this:
package WhateverPackage; use base 'Attribute::Default';
sub what_happened : Default(undef, 'Mister Morton', 'walked down the street') { my ($time, $subject, $verb) = @_;
print "At $time, $subject $verb\n"; }
and $subject
and $verb
will automatically be filled in when
someone calls the what_happened()
subroutine with only a single
argument.
# prints "At 12AM, Mister Morton walked down the street" what_happened('12AM');
# prints "At 3AM, Interplanet Janet walked down the street" what_happened('3AM', 'Interplanet Janet');
# prints "At 6PM, a bill got passed into law" what_happened('6PM', 'a bill', 'got passed into law');
# prints "At 7:03 PM, Mister Morton grew flowers for Perl" what_happened("7:03 PM", undef, "grew flowers for Perl");
You can also use the default mechanism to handle the named parameter
style of coding. Just pass a hash reference as the value of
Default()
, like so:
package YetAnotherPackage; use base 'Attribute::Default';
sub found_pet : Default({name => 'Rufus Xavier Sarsaparilla', pet => 'kangaroo'}) { my %args = @_; my ($first_name) = split(/ /, $args{'name'}, 2); print "$first_name found a $args{'pet'} that followed $first_name home\n"; print "And now that $args{'pet'} belongs...\n"; print "To $args{'name'}.\n\n"; }
# Prints "Rufus found a kangaroo that followed Rufus home"... found_pet();
# Prints "Rafaella found a kangaroo that followed Rafaella home"... found_pet(name => 'Rafaella Gabriela Sarsaparilla');
# Or... found_pet(name => 'Rafaella Gabriela Sarsaparilla', pet => undef);
# Prints "Albert found a rhinoceros that followed Albert home"... found_pet(name => 'Albert Andreas Armadillo', pet => 'rhinoceros');
If you prefer to pass around your arguments as references, rather than
full lists, Attribute::Default can accomodate you. Simply use
Defaults()
instead of Default()
, and your reference parameters
will have defaults added wherever necessary. For example:
package StillAnotherPackage; use base 'Attribute::Default';
sub lally : Defaults({part_of_speech => 'adverbs', place => 'here'}, 3) { my ($in, $number) = @_; print join(' ', ('lally') x $number), ", get your $in->{part_of_speech} $in->{'place'}...\n"; }
# Prints "lally lally lally, get your adverbs here" lally();
# Prints "lally, get your nouns here" lally({part_of_speech => 'nouns'}, 1);
If an argument reference's type does not match an expected default type, then it is passed along without any attempt at defaulting.
If you are performing object-oriented programming, you can use the
:method
attribute to mark your function as a method. The
Default()
and Defaults()
attributes ignore the first argument (in
other words, the 'type' or 'self' argument) for functions marked as
methods. So you can use Default()
and Defaults()
just as for regular functions, like so:
package Thing; use base 'Noun';
sub new :method :Default({ word => 'train' }) { my $type = shift; my %args = @_;
my $self = [ $args->{'word'} ]; bless $self, $type; }
sub make_sentence :method :Default('to another state') { my $self = shift; my ($phrase) = @_;
return "I took a " . $self->[0] . " $phrase" }
# prints "I took a train to another state" my $train = Noun->new(); print $train->make_sentence();
# prints "I took a ferry to the Statue of Liberty" my $ferry = Noun->new( word => 'ferry' ); print $ferry->make_sentence('to the Statue of Liberty');
Sometimes it's not possible to know in advance what the default should
be for a particular argument. Instead, you'd like the default to be
the return value of some bit of Perl code invoked when the subroutine
is called. No problem! You can pass an expanding subroutine to the
Default()
attribute using exsub
, like so:
use Attribute::Default 'exsub'; use base 'Attribute::Default';
sub log_action : Default( undef, exsub { get_time(); } ) { my ($verb, $time) = @_; print "$verb! That's what's happening at $time\n"; }
Here, if $time is undef, it gets filled in with the results of executing get_time().
Exsubs are passed the same arguments as the base subroutine on which they're declared, so you can use other arguments (including default arguments) in your exsubs, like so:
sub double : Default( 2, exsub { $_[0] * 2 }) { my ($first, $second) = @_;
print "First: $first Second: $second\n"; }
# Prints "First: 2 Second: 4" double();
# Prints "First: 3 Second: 6" double(3);
# Prints "First: 4 Second: 5" double(4, 5);
Note that this means that exsubs for methods are effectively called as methods:
package MyObject;
sub new { my $type = shift; bless [3], $type; }
sub double :method :Default( exsub { $_[0][0] * 2 } ) { my $self = shift; print $_[1], "\n"; }
my $myobject = MyObject->new();
# Prints 6 $myobject->double()
# Prints 4 $myobject->double(4);
To avoid potential recursion, other exsub defaults are not passed to exsub arguments.
There's an as-yet unmeasured compile time delay as Attribute::Default does its magic.
Use of large numbers of default arguments to a subroutine can be a sign of bad design. Use responsibly.
Stephen Nelson, <senelson@tdl.com>
Christine Doyle, Randy Ray, Jeff Anderson, and my brother and sister monks at www.perlmonks.org.
the Attribute::Handlers manpage, the Sub::NamedParams manpage, the attributes manpage.
Attribute::Default - Perl extension to assign default values to subroutine arguments |