Perl 5.10 - defined-or and state

[ Perl tips index ]
[ Subscribe to Perl tips ]

Perl Survey Results

For those who took part in the Perl survey, or are interested in the demographics of Perl users throughout the world you can see the Perl Survey results at: http://perlsurvey.org/results/

Some points of interest:


Defined-or

One of the most popular changes in Perl 5.10 is the defined-or operator. This is like Perl's regular || operator, except instead of checking whether a value is true or false, it checks whether that value is defined or not.

Perl's || operator works as follows:

    $a || $b

which means:

    if $a is true
        then return $a
    otherwise
        return $b

As well as being used in conditionals, it can be used to select the first true value from a set of options (or the last option, if none of them are true):

    $x = $a || $b;
    $x = $a || $b || 0;

The || operator is often used to provide a default to a variable, for example:

    # If $coffee_consumed was previously false or undefined,
    # set it to zero instead.
    $coffee_consumed ||= 0;

However the || operator doesn't make any distinction between a value which is undefined, and a value which is defined but false, such as 0 or the empty string "". This can be a problem if we're dealing with a problem domain where zero or the empty string are considered valid values, but we're using undefined to indicate an invalid or absent value.

As an example, let's pretend we're keeping track of how much coffee is consumed by each of our users, and we wish to print UNKNOWN for users whom which we have undefined data. We could try the following:

    $coffee_consumed ||= "UNKNOWN";
    print "$name drank $coffee_consumed cup(s) of coffee today.\n";

However now our non-coffee drinkers (who have $coffee_consumed set to 0) will be reported as drinking an unknown number of cups, even when we know exactly how many cups they've drunk. We can fix this, but it's a lot of work for something that should be easy:

    $coffee_consumed = defined($coffee_consumed) ?
                       $coffee_consumed : "UNKNOWN";
    print "$name drank $coffee_consumed cup(s) of coffee today.\n";

Perl 5.10 provides an elegant solution to this with the new defined-or // operator. It works as follows:

    $a // $b

which can be read as:

    if defined $a
        then return $a
    otherwise
        return $b

Just like ||, // can be used to select the first defined value in a set, or the last value if none of them are defined:

    $x = $a // $b;
    $x = $a // $b // 0;
    $x //= 5;   # same as $x = $x // 5;

This allows us the change the previous example to:

    $coffee_consumed //= "UNKNOWN";
    print "$name drank $coffee_consumed cup(s) of coffee today.\n";

and our data is now handled correctly! Undefined values are replaced with UNKNOWN, but defined values (including zero) aren't touched.

State variables

Traditionally, if we wanted a persistent, private variable in Perl, we'd use a closure:

    {
        my $i = 0;
        sub incrementor {
            return $i++;
        }
    }

This ensured that no other subroutine could access our increment variable and mess things up. Unfortunately the resulting code is not the prettiest in the world; our subroutine is now hidden and indented inside a block.

Perl 5.10 allows us to now declare a lexical variable inside a block which will remember its state between entrances to that block. We can now rewrite our subroutine as follows:

    use feature qw(state);
    sub incrementor {
        state $i = 0;
        return $i++;
    }

The variable $i is set to zero when the program is compiled, and this initialisation does not occur again. Using state can make our code easier to read and understand, and can be particularly useful if we ever need to keep state deep inside a subroutine or other complex structure.

More information

This tip only reveals some of the improvements made in Perl 5.10. Next tip we'll examine Perl's new given and when statements, as well as the smart-match operator.

For further information, we recommend the following resources:

[ Perl tips index ]
[ Subscribe to Perl tips ]


This Perl tip and associated text is copyright Perl Training Australia. You may freely distribute this text so long as it is distributed in full with this Copyright noticed attached.

If you have any questions please don't hesitate to contact us:

Email: contact@perltraining.com.au
Phone: 03 9354 6001 (Australia)
International: +61 3 9354 6001

Valid XHTML 1.0 Valid CSS