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.036; # 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.
So what’s new?
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 (1 = Sun, 7 = 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 = 7
$time->day # Sun - Sat
There are also more obscure pieces of information:
$time->is_leap_year
$time->tzoffset
$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.
More information
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.
What next?
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
36 Comments
The first sample failed for me. I got the following error message:
/Users/bgoggin/pcode->perl thirtysix.pl
Perl v5.360.0 required (did you mean v5.36.0?)–this is only v5.36.0, stopped at
Looks like a typo; could be one of the following:
use v5.36; # Turns on all new 5.36 (and earlier) features
use 5.036; # Turns on all new 5.36 (and earlier) features
Nice article. I’m glad to see others still excited and enthusiastic about Perl. I’ve been using Perl since version 4 on an HPUX system. It is still my goto language to work through a problem or design prototype. Most of the time its performance is good enough for it to be final form.
cheers. Perl is my 1st script language learning. I had a comparison on memory footprint/cpu execution time comparison, between C, bash, perl and python. C is of cause the best thing. Python is the worst in terms of both space/time. Perl is the best.
But the internet is so much popular for python ever since. There have been more articles, more examples, and more 3rd party tools for Python. Python itself is very low efficiency, but the syntax enforcement and 3rd party high efficiency tools around python is very powerful.
Hope Perl can get its way back soon.
Really nice article man. Thank you!
One of your examples is
$time->wday # Sun = 1, Sat = 6
So which day is missing?
Should that be Sun = 0 or Sat = 7?
Sat = 7
Lovely article! 👍
SO happy someone is recognizing Perl! It gets badmouthed so much but it’s really a beautiful language and extremely capable.
The fact that they came up with an entirely new silly function name `say` instead of just adding the feature to `print` already goes to show how ridiculous perl naming is and why it’s hard to take seriously. The code here is so ugly compared to python I see no reason to ever touch it.
a) How do you propose they add such a feature to print() without breaking millions of lines of pre-existing code?
b) print() does have such a feature already, and has had for a long time. It’s called $OUTPUT_RECORD_SEPARATOR
c) Other languages, I surmise, already use two separate functions for output, one with a linebreak and one without. Ruby has print and puts, for one.
In python, print is a function with optional end parameter. Having what I assume global state like $OUTPUT_RECORD_SEPARATOR is bad like C locale and harder to reason about.
Other languages have println, which is a much better name than say or puts. puts having a newline at least matches C behavior (inconsistent legacy behavior since fputs doesn’t), while perl insists on creating another stupid and meaningless function name.
Print in python3 is most hated function ))
`$OUTPUT_RECORD_SEPARATOR` is not a global, it is per filehandle. It contains a string that should be considered to be a newline for that handle. It is usually `\n`. (It’s better and easier to use `$fh->output_record_separator` instead of doing the rigmarole to select the filehandle as the current one, and then change `$OUTPUT_RECORD_SEPARATOR`.)
`println`would be a very bad name for it considering it adds a record separator which may or may not be a newline. Adding a function that can only append a newline is not worth muddying the language for. It’s also a very awkward name for a function. The purpose of the function is to say that the arguments are to be treated as single full record.
`println` represents everything that is wrong with how most languages evolve. Most languages just add new features without necessarily thinking about how it will interact with existing features, or yet to be thought of features.
`say` uses the existing ability to set the output record separator and use it in a new and useful way. It’s use is different than `print` so it is written differently as well.
Having a record separator per handle is a really useful feature. Let’s say that one handle uses a null byte as a terminator, and the other uses a newline. You can quickly and easily transfer data between them by reading one record on one and outputting a record on the other.
Note to moderators: most of what Brad Gilbert has written is factually incorrect as can be seen with reference to the Perl documentation (https://perldoc.perl.org/). It is difficult to tell if Brad is merely overconfident but misinformed, or deliberately trolling. Either way I suggest the Brad’s comment is removed.
I probably wouldn’t have discovered the restarted Perl 7 process or the (new?) blog (blogs.perl.org) for a very long time without this post. I think the Perl blog is a really good idea. It is hard to imagine what some software can really be used for by just reading the release notes.
In any case, I am quietly using a particular Perl script every day to automate some otherwise tedious text processing. And often using some Perl one-liners for ad hoc tasks. And occasional using some other Perl scripts for various tasks. Python can’t (really) do one-liners, so this is a superpower to have when Perl has a perception problem.
It is also wicked fast. Even for relatively complex text processing it is often I/O limited (at least for rotating rust (yes, I know, but, ironically, rotating rust is not subject to catastrophic failure)). More than 50 MB / second is typical.
Perl 7 was announced on 2020-06-24 (quote, ahem… “…about six months when the first release candidate should appear”), but then nothing happened. The Perl community ought to reach consensus before announcements like this. This presentation from 2012 (YAPC 2012) is quite telling (and entertaining!).
45 minutes of marginalizing an entire community based on skin color and gender? disgusting. This doesn’t exactly want to make me learn perl (then again, I’d be considered ‘less desirable’ based on my skin color and gender). whew. no thank you.
Please don’t bother your grandfather with this.
He used languages with lexically scoped variables without sigils that could be filehandles if you wanted them to, printing functions or statements that add a newline, and time formatting functions as member functions in a class.
He used perl too, and has since moved on.
You should use Modern::Perl to include all the features of modern Perl. https://metacpan.org/pod/Modern::Perl
“That Perl interpreter you have on your Linux machine? Update it and check out the present.”
But then I’d have to… y’know… use _Perl…_
Exactly. The only reason I have it installed is because someone else’s package from 20 years ago needs it.
I just wanted to add that you do not need to install a local version of Perl for most of these features: They have been present since Perl 5.10 or earlier. Perl 5.10 was released in 2007 (and bugfixed in 2009 IIRC) and contains everything mentioned in this post except function signatures.
So you can use the system Perl if you want — it is very capable too.
Hi Gogh,
Are you saying the blog is trotting out 15-year-old features of Perl as new?
That explains it; if they were new features, it would be “not your father’s Perl”, right?
Things that were new 15 years ago are “not your grandfather’s”.
Makes sense.
There is, or at least used to be, a lot of Perl tutorials online that write Perl in the 1990s way, back when Perl 5 was barely new, when Perl did not have any of these features. Perl has evolved beyond that, a long time ago. But many coders haven’t. They learnt from the old tutorials, tutorials whose authors only had the smallest amounts of understanding of good programming, tutorials that were using bad programming practices. That is part of the language’s bad rap. That is why the post is referring to the grandfather’s Perl — the old 1990s ways of writing Perl.
It’s like a large part of the programmer population today was still writing K&R C from the 1970s when ANSI C and C99 happened two or three decades ago.
The popular view of Perl is to judge it based on these samples of 1990s-esque code. This blog post is supposed to correct that view.
Is this a joke? You’re telling me that in fifteen years they have:
* Added a function that prints out a string followed by a newline;
* Changed strftime from a free function to a member function of their tm struct,
* Given us the ability to store file handles in locals instead of globals, and
* Added syntactic sugar for function signatures
The fact that a language that came out in 1987 didn’t ship with all this stuff out of the box is absurd, and the fact that they’ve put it in 35 years later isn’t much of an argument for using the langauge…
Are you aware that the first version of Perl was little more than a nicer to use shell scripting language?
It had almost no features.
It didn’t even have arrays of arrays until ’94. (You could have faked it with symbolic referencing, but that is nowhere near as good.)
It has evolved more than any other single language has, and has done so without breaking most existing code.
No it’s not a joke. Like another reply alluded to, many folks’ view of Perl is based on things learned in the 1990s, and this article points out that Perl has evolved since then. Much of that evolution happened many years ago, but there are still plenty of people whose knowledge of Perl is decades out of date. Other languages don’t have this problem because they either haven’t been around as long or they haven’t evolved.
Nice post, thanks.
Funny how Perl posts like these are like troll fly paper. Just bullies on a playground, putting down the other kids so they can somehow try to feel better about themselves and, in this case, their programming language choices. Sad.
-s1m0n-
I’m surprised they used “say” for what is basically println everywhere else I’ve seen it. Really odd decision
You’re right that it is an odd decision to use `println`, that’s why Perl doesn’t use it. I mean `say` adds a per-handle output record separator, which may or may not be a newline. So it would be a bad idea to name it `println`.
It also wouldn’t be worth it to add a simple function that only adds a newline. That would be polluting the namespace for very little gain.
(Yes I’m aware that you meant it the other way ’round.)
I was also curious about why “say” instead of something like “println” (I’m a primarily a Java programmer as you might have guessed 😀 ), as “say” didn’t really offer any semantic hint to me about what extra it’s doing.
Your explanation about it adding a “per-handle output record separator” makes sense to me though as to why “println” would be a bad name, thanks for the insight.
River’s comment below about “say” being 3 chars and thus quick to type also makes sense for this choice of name 🙂
Seems pretty good to me. Also “say” can be typed with three keystrokes versus seven for “println”
Please, let perl die and don’t try to resurrect it. We have suffered too much pain and we don’t want to go through it again.
Perl has yet to die. It has continued to evolve for decades.
It has evolved more than any other single language has evolved.
I continue to use Perl, because using anything else is too painful. I’m always missing quality of life features that Perl has that other languages often don’t.
If you had painful memories of using Perl, it’s because you didn’t have a good teacher to teach you how to use it. Or you were using a very old version.
I’m no londer useing perl, but i was a PerlMonger goup creator and leader.
I have the feeling this article could be written 10 years ago. What is there new from the last 10 years?
Perhaps the main one in this post is that function signatures with variables are no longer experimental.
From looking over PerlDelta for the latest release, here are a couple changes over the past year.
– Iterating over multiple values at a time (experimental)
You can now iterate over multiple values at a time by specifying a list of lexicals within parentheses. For example,
for my ($key, $value) (%hash) { … }
for my ($left, $right, $gripping) (@moties) { … }
– defer blocks (experimental)
This release adds support for defer blocks, which are blocks of code prefixed by the defer modifier. They provide a section of code which runs at a later time, during scope exit.