This is not your grandfather’s Perl

If you were to search the internet for recent articles about Perl, you might well be led to believe that the language hasn’t changed in the last twenty years. And, sadly, that’s a completely understandable belief as the major version number hasn’t changed since 1994.

In July 2000, Perl 6 was announced as the next version of Perl. Over the next several years, the scope of the project expanded until it became obvious that Perl 6 wasn’t going to be an evolution of Perl 5 but, rather, a revolutionary new language with its roots in Perl. The narrative was then changed to describe Perl 6 as a new language in the Perl family.

A production version of Perl 6 was released at the end of 2015 and, in 2019, the developers bowed to the inevitable and renamed the language to Raku in order to emphasise its break from Perl.

All of this meant that for almost twenty years, Perl had no next version number to use. And this has, unsurprisingly, led to a large part of the industry assuming that Perl hasn’t changed much over that time. This is unfortunate as Perl has undergone massive changes in the new millennium. The Perl 5 team have developed an annual release cycle, where a new version is released in about May of every year. Version 5.36 was released in May 2022 and is very different to version 5.6.0, which was current back in the summer of 2000. In this article, we’ll look at some of the new Perl features that you might have missed.

Getting a newer version

Most Linux systems will come with a version of Perl already installed. Depending on which distribution and which version of that distribution you’re using, you’ll get different versions of Perl. Sometimes, these are rather old versions of Perl. I recommend that you free yourself from the constraint of using the system version of Perl. Not only does this let you choose the version of Perl you’re using, but it also prevents you from polluting the system Perl by installing extensions that might not be compatible with it. As the system Perl is often used to perform important system maintenance tasks, you obviously want to keep it running as smoothly as possible.

For that reason, it’s usually a good idea to install your own version of Perl. There are a few ways to do that. The most popular approaches are to build and install your own version of Perl (this is often installed to /opt/perl) or to run your application in a separate container using Docker. For either of these options, the Perl team has tools that will help you. Perlbrew is a tool for installing and managing multiple versions of Perl and there are a number of official Perl images available from the Docker hub.

Accessing new features

The Perl development team thinks that backwards compatibility is very important. They will do anything in their power to ensure that new versions of Perl won’t break old code. Most of the time they manage that; occasionally they don’t.

But this policy means that many new Perl features are hidden away behind feature guards and aren’t available unless you explicitly turn them on—the argument being that if you’re knowledgeable enough to turn a particular feature on, then you’re also knowledgeable enough to deal with any incompatibilities that the new feature introduces.

There are two ways to turn on features. If you add “use <version>” to your code, then it will turn on all of the features that were introduced in that version of Perl (and all previous versions). Alternatively, all protected features have a name that can be used with “use feature <name>” to turn on that specific feature. For example, if you want to use the say()command (which we’ll cover in the next section), you can either use:

  use 5.36; # Turns on all new 5.36 (and earlier) features

Or you can use:

  use feature 'say'; # Just turns on the "say" feature

If you try to turn on features that are too new for your version of Perl, you’ll get a fatal error as the program starts up.

Ok, so we’ve got ourselves access to a newer version of Perl. What’s the fuss about? What new features can we use? Let’s start with a tiny feature that I use in most of the Perl programs I write.

say()

You’re no doubt familiar with using print() to display data on the console or to write it to a file. Perl 5.10 introduced the say() command which does the same thing but automatically adds a newline character to the output.

It sounds like a small thing, but it’s surprisingly useful. How many times do you print a line of data to a file and have to remember to explicitly add the newline? This just makes your life a little bit easier. Instead of typing:

    print "This is some data: $data\n";

You can use:

    say "This is some data: $data";

This was added in Perl 5.10 and can be enabled with use feature 'say'.

Lexical filehandles

Some of the improvements were needed because in places Perl’s Unix/C heritage shows through a little more than we’d like it to in the 21st century. One good example of this is bareword filehandles. You’re probably used Perl code to open a file looking like this:

    open FH, 'filename.dat' or die;

Here, we open the file “filename.dat” and associate that file with a filehandle called FH. We can then read data from the file by reading from the filehandle FH.

The problem is that FH is a variable. It doesn’t look like a variable because it doesn’t start with a $, @ or % symbol like most Perl variables do. But it is a variable. And, worst than that, it’s a package variable (which is the closest thing that Perl has to a global variable). You might think that because you open and read your file inside a subroutine then you are using a filehandle which only exists inside that subroutine. But no. You’ve created a filehandle that’s available pretty much anywhere inside your program. And that’s not good programming practice.

So for a long time (back to at least Perl 5.6), it has been possible to open filehandles and store them in lexical variables (the type of Perl variable that only exists in a particular block of code and, therefore, the most sensible type of variable to use in most cases). The rule is that if you use a scalar variable that contains no data (is “undefined” in Perl parlance) in place of a filehandle in a call to open(), then the filehandle is stored in that scalar variable. The easiest way to ensure a variable is undefined is to declare it where you first want to use it, so the code above can be replaced by something like this:

    # lexical variables are declared using "my"
    open my $fh, 'filename.dat' or die;

You can then use $fh in exactly the same way as you used FH in the previous code with the benefit that you’re not polluting your symbol table with unwanted variables.

There’s another advantage too. The variable $fh is scoped to the block where it’s declared. When you leave that block, the variable goes out of scope and is destroyed. For a scalar variable containing a filehandle, the file connected to the filehandle is automatically closed, giving you one fewer thing to think about.

Date and time handling

For a long time, Perl’s standard functions for dealing with dates and times were also very tied to its Unix roots. You may have seen code like this:

    my @datetime = localtime();

The localtime() function returns a list of values that represent the various parts of the current local time. The list of values you get back are:

  • Seconds
  • Minutes
  • Hours
  • Day of the month
  • Month number (0-11)
  • Year (actually the year – 1900)
  • Day of the week (0 = Sun, 6 = Sat)
  • Day of the year
  • Boolean flag indicating if daylight saving time is in operation

That’s a lot to deal with and those are some very weird sets of values. If you wanted to produce an ISO8601 compliant timestamp you would need something like this:

    printf '%4d-%02d-%02dT%02d:%02d:%02d,
           $date[5]+1900, $date[4]+1, $date[3],
           $date[2], $date[1], $date[0];

Or perhaps you know about the relatively obscure strftime() function that is hidden away in the POSIX library.

    use POSIX 'strftime';
    print strftime('%Y-%m-%dT%H:%M:%S', localtime());

The strftime() function takes a format string definition, followed by a list of values describing a date/time that conveniently has exactly the same foibles as the values returned by localtime(). It all works together if you know how these functions work. But very few people seem to know that.

Since Perl 5.10, the standard library has included a module called Time::Piece. When you use Time::Piece in your code, it overrides localtime() and replaces it with a function that returns an object that contains details of the current time and date. That object has a strftime() method;

    use Time::Piece;

    my $time = localtime;
    print  $time->strftime('%Y-%m-%dT%H:%M:%S');

And it also has several other methods for accessing information about the time and date. This include:

    $time->min
    $time->hour
    $time->year # The actual year
    $time->mon # 1 - 12
    $time->month # Jan - Dec
    $time->wday # Sun = 1, Sat = 6
    $time->day # Sun - Sat

There are also more obscure pieces of information:

    $time->is_leap_year
    $time->txoffset
    $time->julian_day

Using Time::Piece will almost certainly make your date and time handling code easier to write and (more importantly) easier to read and understand.

Function signatures

Another area where Perl puzzles people who are used to more traditional languages is in the handling of parameters to subroutines. In Perl, all parameters are passed into a subroutine in an array called @_ and it’s up to the programmer to extract the data from that array. So almost every Perl subroutine will start with code that looks like this:

    sub my_subroutine 
      my ($foo, $bar, $baz) = @_;

      …
    

This takes the first three values from @_ and stores them in variables called $foo, $bar and $baz. You can then use those variables to do whatever you need your subroutine to do. Of course, you don’t have to use these variables; you could use the values from the array directly. But as that would leave you with variables called $_[0], $_[1] and $_[2], that’s probably not going to lead to particularly readable code. You can see why most people like to copy the values from the array into variables at the start of the subroutine.

Of course, that’s not how most languages do it. In most languages you’d have a list of variable names after the subroutine name and the parameters would be passed directly into those. Well, as of version 5.36 (which was released earlier this summer) Perl has that too.

You turn the feature on with use feature 'signatures' and it looks like this:

    use feature ('say', 'signatures');

    sub my_subroutine ($foo, $bar, $baz) 
      say "$foo - $bar - $baz";
    

    my_subroutine('sound', 'and', 'vision');

The three parameters you pass into the subroutine are copied directly into the variables as the subroutine is called. You no longer need to do it yourself.

Subroutine signatures have many other features. You can, for example, declare default values for parameters.

    use feature ('say', 'signatures');

    sub my_subroutine ($foo, $bar, $baz = 'fury') 
      say "$foo - $bar - $baz";
    

     my_subroutine('sound', 'and');

In this case, if you don’t pass the third parameter to the subroutine, then $baz will be given the default value of “fury”.

You can also pass in an array (or a list of values that will be stored in an array):

    sub my_subroutine ($foo, $bar, @baz) 
      say "$foo - $bar - ", join(':', @baz);
    

    my_subroutine('some', 'numbers', 1 .. 10);

And something very similar works for hashes too:

    sub my_subroutine (%params) 
      for my $pname (keys %params) 
        say "$pname -> $params$pname";
      
    

    my_subroutine( run => 1, dir => '/home/me');

There are plenty of other features. See the perlsub manual page for the full details.

In this article, I’ve just scratched the surface of the changes that have happened to Perl in the last fifteen years. There are plenty of other improvements to find out about. 

Each new version of Perl comes with a “perldelta” manual page which explains the differences from the previous version. So the perldelta that comes with version 5.36 describes everything that has changed since version 5.34 (Perl uses odd version numbers to indicate development versions, so it’s only even numbers that count as production releases). Each version will also include the perldeltas from all of the previous versions (well, back to version 5.4, which was the first version to include this documentation). The latest version is always called just “perldelta” and the older ones are renamed to include the version number. So the document that describes the differences between 5.32 and 5.34 is called “perl5340delta”.

If you’re interested in Perl, then it’s well worth taking the time to at least skim these documents so you have an idea of the kinds of new features that have been added to Perl. Because most of the articles you’ll find on the web seem to be written by people who don’t know about these improvements.

Perl is always moving forward, so I’d just like to end by hinting at a couple of things that might be coming in the future.

Probably the most interesting project that is currently being undertaken by the Perl development team is Corinna. This is a brand new object-oriented programming framework that is being written into the Perl core. The current Perl OO framework is very powerful and flexible, but it’s a bit bare-bones. Corinna aims to bring the best of current OO theory to Perl. Development has been underway for a couple of months. The current plan is for a basic version to be included in Perl 5.38, which will be released in the second quarter of next year. This will be in addition to Perl’s existing OO framework—too much code relies on that for anyone to consider removing it.

Beyond that, the Perl development team have their eye on a major version number bump. The existence of Perl 6 (even if it’s now called Raku) means that we won’t use that version number to avoid confusing people. The next major version of Perl will be Perl 7. A few months ago, the Perl Steering Council published a blog post talking about their plans for the future of Perl, including Perl 7. Basically, they are saying that they will continue to add new features to Perl and at some point they could well decide that enough features have been added to make it worthwhile to bump the major version. They don’t explicitly say it, but I suspect Corinna might well be seen as a large enough change to the language to make the version number change likely.

All in all, I believe that the development of the Perl language is in a very healthy state and it looks like that is likely to continue for the foreseeable future.

Tags: perl