Rate this page

Flattr this

Accept named function arguments in Perl

Tested on

Debian (Lenny)
Ubuntu (Precise, Trusty)

Objective

Within a Perl function, to accept a list of named arguments

Background

Perl supports two types of function argument: named and unnamed. An example of the use of unnamed arguments is the built-in function substr:

my $substring = substr($string,$index,$length);

The meaning of each argument is determined by its position in the list. The arguments must therefore occur in a fixed order, and omissions are possible only at the end of the list. This is a convenient and concise notation for functions that take one or two arguments, but its readability diminishes rapidly as the list becomes longer. It is particularly inconvenient for functions that have a large number of optional, rarely-used arguments.

An example of the use of named arguments is the constructor for the DateTime class:

my $dt = DateTime->new(
    year   => 2011,
    month  => 5,
    day    => 21,
    hour   => 18,
    minute => 0);

Some of the optional arguments have been omitted here (such as second and nanosecond, which default to zero). Advantages of naming are that it makes the argument list self-documenting and reduces the risk of values being accidentally omitted or transposed.

Scenario

Suppose that you wish to write a function called add_label to add a label to a diagram. Each label has seven properties: x, y, angle, text, style, weight and colour. All of these except for x, y and text have sensible defaults that will often not need to be changed.

You wish to write the function so that it takes named arguments. For example, it should be possible to call it in the following manner:

add_label(x=>100,y=>200,text=>'Widgets',weight=>'bold');

Method

The only difference between the => operator and a comma is that => forces the word to its left to be interpreted as a string. It follows that the argument list:

(x=>100,y=>200,text=>'Widgets',weight=>'bold')

is equivalent to:

('x',100,'y',200,'text','Widgets','weight','bold')

Being an array this can be passed into the function in the normal way, becoming the initial value of @_. Once there it can be used to initialise a hash:

sub add_label {
    my %args = @_;
    # ...
}

The conversion from array to hash will correctly interpret the argument list as an alternating sequence of names and values. The value of a particular argument can be inspected by using its name as a key to the hash:

my $text = $args{text};

If the same name were to appear twice in the argument list then the final instance would override any earlier ones. This behaviour can be exploited to implement default values:

sub add_label {
    my %args = (
        angle => 0,
        style => 'normal',
        weight => 'normal',
        colour => 'black',
        @_);

    # ...
}

Including one array within another causes them to merge, so if the argument list passed into the function is:

(x=>100,y=>200,text=>'Widgets',weight=>'bold')

then the value used to initialise the hash will be:

(angle=>0,style=>'normal',weight=>'normal',colour=>'black',
    x=>100,y=>200,text=>'Widgets',weight=>'bold')

The style and colour arguments will thus take their default values, but the default for the weight argument will be overridden.

Variations

Named Method Arguments

If a subroutine is an object method then its first argument will be an unnamed reference to the object on which to act. This does not prevent the remainder of the list containing named arguments provided that the object reference is removed before the array is converted to a hash. That can be done using the shift function:

sub Label::add {
    my $self = shift;
    my %args = (
        angle => 0,
        style => 'normal',
        weight => 'normal',
        colour => 'black',
        @_);

    # ...
}

Tags: perl