The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Attribute::Default - Perl extension to assign default values to subroutine arguments

SYNOPSIS

  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');

DESCRIPTION

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.

SIMPLE DEFAULTS

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');

DEFAULTING REFERENCES

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.

DEFAULTING METHOD ARGUMENTS

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');

EXPANDING SUBROUTINES

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.

BUGS

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.

AUTHOR

Stephen Nelson, <senelson@tdl.com>

SPECIAL THANKS TO

Christine Doyle, Randy Ray, Jeff Anderson, and my brother and sister monks at www.perlmonks.org.

SEE ALSO

Attribute::Handlers, Sub::NamedParams, attributes.